---
openapi: 3.0.3
info:
  title: TrustGrowth API
  version: v1
  description: TrustGrowth API for public proof data plus authenticated site, keyword,
    content, and audit operations. `X-RateLimit-*` and `Retry-After` headers are authoritative
    for the current request; body `meta.rate_limit` is retained for client convenience.
servers:
- url: https://www.trustgrowth.ai
security:
- bearerAuth: []
tags:
- name: Public Proofs
  description: Public machine-readable proof endpoints for proof-eligible sites.
- name: Sites
  description: Site-scoped TrustGrowth API endpoints.
- name: Campaigns
  description: Read-only campaign and mission-control endpoints. Requires the `campaigns`
    plan feature (Growth/Admin).
- name: MCP
  description: MCP discovery endpoints for the TrustGrowth API.
paths:
  "/api/v1/proofs/{slug}":
    get:
      operationId: getPublicProof
      tags:
      - Public Proofs
      summary: Get public proof data
      description: Returns the canonical machine-readable proof payload for a proof-eligible
        public site.
      security: []
      parameters:
      - name: slug
        in: path
        required: true
        schema:
          type: string
        description: Site slug.
      responses:
        '200':
          description: ok
          headers:
            X-RateLimit-Limit:
              schema:
                type: integer
              description: Request limit for the active window.
            X-RateLimit-Remaining:
              schema:
                type: integer
              description: Requests remaining after this request.
            X-RateLimit-Reset:
              schema:
                type: integer
              description: Unix time when the active window resets.
            Retry-After:
              schema:
                type: integer
              description: Seconds until retry when the request is rate limited.
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/PublicProofResponse"
        '404':
          description: Not found
          headers:
            X-RateLimit-Limit:
              schema:
                type: integer
              description: Request limit for the active window.
            X-RateLimit-Remaining:
              schema:
                type: integer
              description: Requests remaining after this request.
            X-RateLimit-Reset:
              schema:
                type: integer
              description: Unix time when the active window resets.
            Retry-After:
              schema:
                type: integer
              description: Seconds until retry when the request is rate limited.
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
  "/api/v1/sites":
    get:
      operationId: listSites
      tags:
      - Sites
      summary: List sites
      description: Returns the authenticated user's sites with current overall scores.
      security:
      - bearerAuth: []
      parameters:
      - name: page
        in: query
        required: false
        schema:
          type: integer
          minimum: 1
        description: Page number.
      - name: per_page
        in: query
        required: false
        schema:
          type: integer
          minimum: 1
          maximum: 100
        description: Items per page; defaults to 25.
      responses:
        '200':
          description: ok
          headers:
            X-RateLimit-Limit:
              schema:
                type: integer
              description: Request limit for the active window.
            X-RateLimit-Remaining:
              schema:
                type: integer
              description: Requests remaining after this request.
            X-RateLimit-Reset:
              schema:
                type: integer
              description: Unix time when the active window resets.
            Retry-After:
              schema:
                type: integer
              description: Seconds until retry when the request is rate limited.
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/SitesIndexResponse"
        '401':
          description: Unauthorized
          headers:
            X-RateLimit-Limit:
              schema:
                type: integer
              description: Request limit for the active window.
            X-RateLimit-Remaining:
              schema:
                type: integer
              description: Requests remaining after this request.
            X-RateLimit-Reset:
              schema:
                type: integer
              description: Unix time when the active window resets.
            Retry-After:
              schema:
                type: integer
              description: Seconds until retry when the request is rate limited.
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
        '403':
          description: Forbidden
          headers:
            X-RateLimit-Limit:
              schema:
                type: integer
              description: Request limit for the active window.
            X-RateLimit-Remaining:
              schema:
                type: integer
              description: Requests remaining after this request.
            X-RateLimit-Reset:
              schema:
                type: integer
              description: Unix time when the active window resets.
            Retry-After:
              schema:
                type: integer
              description: Seconds until retry when the request is rate limited.
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
  "/api/v1/sites/{slug}/score":
    get:
      operationId: getSiteScore
      tags:
      - Sites
      summary: Get site score
      description: Returns the raw current or historical daily-score breakdown for
        a site. Operator workflows should prefer the publication evidence packet's
        operator_summary contract when they need the operator-safe score snapshot.
      security:
      - bearerAuth: []
      parameters:
      - name: slug
        in: path
        required: true
        schema:
          type: string
        description: Site slug.
      - name: date
        in: query
        required: false
        schema:
          type: string
          format: date
        description: Optional score date in YYYY-MM-DD format.
      responses:
        '200':
          description: ok
          headers:
            X-RateLimit-Limit:
              schema:
                type: integer
              description: Request limit for the active window.
            X-RateLimit-Remaining:
              schema:
                type: integer
              description: Requests remaining after this request.
            X-RateLimit-Reset:
              schema:
                type: integer
              description: Unix time when the active window resets.
            Retry-After:
              schema:
                type: integer
              description: Seconds until retry when the request is rate limited.
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ScoreResponse"
        '401':
          description: Unauthorized
          headers:
            X-RateLimit-Limit:
              schema:
                type: integer
              description: Request limit for the active window.
            X-RateLimit-Remaining:
              schema:
                type: integer
              description: Requests remaining after this request.
            X-RateLimit-Reset:
              schema:
                type: integer
              description: Unix time when the active window resets.
            Retry-After:
              schema:
                type: integer
              description: Seconds until retry when the request is rate limited.
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
        '403':
          description: Plan limited or coming soon
          headers:
            X-RateLimit-Limit:
              schema:
                type: integer
              description: Request limit for the active window.
            X-RateLimit-Remaining:
              schema:
                type: integer
              description: Requests remaining after this request.
            X-RateLimit-Reset:
              schema:
                type: integer
              description: Unix time when the active window resets.
            Retry-After:
              schema:
                type: integer
              description: Seconds until retry when the request is rate limited.
          content:
            application/json:
              schema:
                oneOf:
                - "$ref": "#/components/schemas/PlanLimitErrorResponse"
                - "$ref": "#/components/schemas/ComingSoonErrorResponse"
        '404':
          description: Not found
          headers:
            X-RateLimit-Limit:
              schema:
                type: integer
              description: Request limit for the active window.
            X-RateLimit-Remaining:
              schema:
                type: integer
              description: Requests remaining after this request.
            X-RateLimit-Reset:
              schema:
                type: integer
              description: Unix time when the active window resets.
            Retry-After:
              schema:
                type: integer
              description: Seconds until retry when the request is rate limited.
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
        '422':
          description: Invalid params
          headers:
            X-RateLimit-Limit:
              schema:
                type: integer
              description: Request limit for the active window.
            X-RateLimit-Remaining:
              schema:
                type: integer
              description: Requests remaining after this request.
            X-RateLimit-Reset:
              schema:
                type: integer
              description: Unix time when the active window resets.
            Retry-After:
              schema:
                type: integer
              description: Seconds until retry when the request is rate limited.
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
  "/api/v1/sites/{slug}/authority":
    get:
      operationId: getSiteAuthority
      tags:
      - Sites
      summary: Get authority evidence
      description: Returns persisted TrustGrowth authority and provider-labeled DataForSEO
        backlink evidence. Measurements older than 35 days are nulled; this read never
        triggers collection or consumes credits.
      security:
      - bearerAuth: []
      parameters:
      - name: slug
        in: path
        required: true
        schema:
          type: string
        description: Site slug.
      responses:
        '200':
          description: ok
          headers:
            X-RateLimit-Limit:
              schema:
                type: integer
              description: Request limit for the active window.
            X-RateLimit-Remaining:
              schema:
                type: integer
              description: Requests remaining after this request.
            X-RateLimit-Reset:
              schema:
                type: integer
              description: Unix time when the active window resets.
            Retry-After:
              schema:
                type: integer
              description: Seconds until retry when the request is rate limited.
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/AuthorityResponse"
        '401':
          description: Unauthorized
          headers:
            X-RateLimit-Limit:
              schema:
                type: integer
              description: Request limit for the active window.
            X-RateLimit-Remaining:
              schema:
                type: integer
              description: Requests remaining after this request.
            X-RateLimit-Reset:
              schema:
                type: integer
              description: Unix time when the active window resets.
            Retry-After:
              schema:
                type: integer
              description: Seconds until retry when the request is rate limited.
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
        '403':
          description: Plan limited or coming soon
          headers:
            X-RateLimit-Limit:
              schema:
                type: integer
              description: Request limit for the active window.
            X-RateLimit-Remaining:
              schema:
                type: integer
              description: Requests remaining after this request.
            X-RateLimit-Reset:
              schema:
                type: integer
              description: Unix time when the active window resets.
            Retry-After:
              schema:
                type: integer
              description: Seconds until retry when the request is rate limited.
          content:
            application/json:
              schema:
                oneOf:
                - "$ref": "#/components/schemas/PlanLimitErrorResponse"
                - "$ref": "#/components/schemas/ComingSoonErrorResponse"
        '404':
          description: Not found
          headers:
            X-RateLimit-Limit:
              schema:
                type: integer
              description: Request limit for the active window.
            X-RateLimit-Remaining:
              schema:
                type: integer
              description: Requests remaining after this request.
            X-RateLimit-Reset:
              schema:
                type: integer
              description: Unix time when the active window resets.
            Retry-After:
              schema:
                type: integer
              description: Seconds until retry when the request is rate limited.
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
  "/api/v1/sites/{slug}/visibility":
    get:
      operationId: getSiteVisibility
      tags:
      - AI visibility
      summary: Get persisted AI visibility
      description: Returns the fixed five-stage visibility funnel from persisted captures,
        preserving measurement nature, status, provenance, uncertainty, and independent
        plan gates. This operation never triggers capture work.
      security:
      - bearerAuth: []
      parameters:
      - name: slug
        in: path
        required: true
        schema:
          type: string
        description: Site slug.
      responses:
        '200':
          description: ok
          headers:
            X-RateLimit-Limit:
              schema:
                type: integer
              description: Request limit for the active window.
            X-RateLimit-Remaining:
              schema:
                type: integer
              description: Requests remaining after this request.
            X-RateLimit-Reset:
              schema:
                type: integer
              description: Unix time when the active window resets.
            Retry-After:
              schema:
                type: integer
              description: Seconds until retry when the request is rate limited.
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/SiteVisibilityResponse"
        '401':
          description: Unauthorized
          headers:
            X-RateLimit-Limit:
              schema:
                type: integer
              description: Request limit for the active window.
            X-RateLimit-Remaining:
              schema:
                type: integer
              description: Requests remaining after this request.
            X-RateLimit-Reset:
              schema:
                type: integer
              description: Unix time when the active window resets.
            Retry-After:
              schema:
                type: integer
              description: Seconds until retry when the request is rate limited.
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
        '403':
          description: Forbidden
          headers:
            X-RateLimit-Limit:
              schema:
                type: integer
              description: Request limit for the active window.
            X-RateLimit-Remaining:
              schema:
                type: integer
              description: Requests remaining after this request.
            X-RateLimit-Reset:
              schema:
                type: integer
              description: Unix time when the active window resets.
            Retry-After:
              schema:
                type: integer
              description: Seconds until retry when the request is rate limited.
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
        '404':
          description: Not found
          headers:
            X-RateLimit-Limit:
              schema:
                type: integer
              description: Request limit for the active window.
            X-RateLimit-Remaining:
              schema:
                type: integer
              description: Requests remaining after this request.
            X-RateLimit-Reset:
              schema:
                type: integer
              description: Unix time when the active window resets.
            Retry-After:
              schema:
                type: integer
              description: Seconds until retry when the request is rate limited.
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
  "/api/v1/sites/{slug}/issues":
    get:
      operationId: listSiteIssues
      tags:
      - Sites
      summary: List site issues
      description: Returns site audit issues. Open issues default to the latest actionable
        scope used by summary and next_actions; pass scope=backlog to inspect the
        full open backlog.
      security:
      - bearerAuth: []
      parameters:
      - name: slug
        in: path
        required: true
        schema:
          type: string
        description: Site slug.
      - name: status
        in: query
        required: false
        schema:
          type: string
          enum:
          - open
          - fixed
          - all
        description: Issue status filter.
      - name: scope
        in: query
        required: false
        schema:
          type: string
          enum:
          - actionable
          - backlog
        description: Issue scope. Open issues default to actionable; fixed/all default
          to backlog.
      - name: severity
        in: query
        required: false
        schema:
          type: string
          enum:
          - critical
          - warning
          - info
        description: Issue severity filter.
      - name: page
        in: query
        required: false
        schema:
          type: integer
          minimum: 1
        description: Page number.
      - name: per_page
        in: query
        required: false
        schema:
          type: integer
          minimum: 1
          maximum: 100
        description: Items per page; defaults to 25.
      responses:
        '200':
          description: ok
          headers:
            X-RateLimit-Limit:
              schema:
                type: integer
              description: Request limit for the active window.
            X-RateLimit-Remaining:
              schema:
                type: integer
              description: Requests remaining after this request.
            X-RateLimit-Reset:
              schema:
                type: integer
              description: Unix time when the active window resets.
            Retry-After:
              schema:
                type: integer
              description: Seconds until retry when the request is rate limited.
          content:
            application/json:
              schema:
                type: object
                required:
                - data
                - meta
                properties:
                  data:
                    type: array
                    items:
                      "$ref": "#/components/schemas/AuditIssue"
                  meta:
                    "$ref": "#/components/schemas/Meta"
        '401':
          description: Unauthorized
          headers:
            X-RateLimit-Limit:
              schema:
                type: integer
              description: Request limit for the active window.
            X-RateLimit-Remaining:
              schema:
                type: integer
              description: Requests remaining after this request.
            X-RateLimit-Reset:
              schema:
                type: integer
              description: Unix time when the active window resets.
            Retry-After:
              schema:
                type: integer
              description: Seconds until retry when the request is rate limited.
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
        '403':
          description: Plan limited or coming soon
          headers:
            X-RateLimit-Limit:
              schema:
                type: integer
              description: Request limit for the active window.
            X-RateLimit-Remaining:
              schema:
                type: integer
              description: Requests remaining after this request.
            X-RateLimit-Reset:
              schema:
                type: integer
              description: Unix time when the active window resets.
            Retry-After:
              schema:
                type: integer
              description: Seconds until retry when the request is rate limited.
          content:
            application/json:
              schema:
                oneOf:
                - "$ref": "#/components/schemas/PlanLimitErrorResponse"
                - "$ref": "#/components/schemas/ComingSoonErrorResponse"
        '404':
          description: Not found
          headers:
            X-RateLimit-Limit:
              schema:
                type: integer
              description: Request limit for the active window.
            X-RateLimit-Remaining:
              schema:
                type: integer
              description: Requests remaining after this request.
            X-RateLimit-Reset:
              schema:
                type: integer
              description: Unix time when the active window resets.
            Retry-After:
              schema:
                type: integer
              description: Seconds until retry when the request is rate limited.
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
        '422':
          description: Invalid params
          headers:
            X-RateLimit-Limit:
              schema:
                type: integer
              description: Request limit for the active window.
            X-RateLimit-Remaining:
              schema:
                type: integer
              description: Requests remaining after this request.
            X-RateLimit-Reset:
              schema:
                type: integer
              description: Unix time when the active window resets.
            Retry-After:
              schema:
                type: integer
              description: Seconds until retry when the request is rate limited.
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
  "/api/v1/sites/{slug}/snapshots":
    get:
      operationId: listSiteSnapshots
      tags:
      - Sites
      summary: List site snapshots
      description: Returns daily, weekly, or monthly site performance snapshots.
      security:
      - bearerAuth: []
      parameters:
      - name: slug
        in: path
        required: true
        schema:
          type: string
        description: Site slug.
      - name: from
        in: query
        required: false
        schema:
          type: string
          format: date
        description: Inclusive start date.
      - name: to
        in: query
        required: false
        schema:
          type: string
          format: date
        description: Inclusive end date.
      - name: granularity
        in: query
        required: false
        schema:
          type: string
          enum:
          - daily
          - weekly
          - monthly
        description: Optional time bucket size.
      responses:
        '200':
          description: ok
          headers:
            X-RateLimit-Limit:
              schema:
                type: integer
              description: Request limit for the active window.
            X-RateLimit-Remaining:
              schema:
                type: integer
              description: Requests remaining after this request.
            X-RateLimit-Reset:
              schema:
                type: integer
              description: Unix time when the active window resets.
            Retry-After:
              schema:
                type: integer
              description: Seconds until retry when the request is rate limited.
          content:
            application/json:
              schema:
                type: object
                required:
                - data
                - meta
                properties:
                  data:
                    type: array
                    items:
                      "$ref": "#/components/schemas/DailySnapshot"
                  meta:
                    "$ref": "#/components/schemas/Meta"
        '401':
          description: Unauthorized
          headers:
            X-RateLimit-Limit:
              schema:
                type: integer
              description: Request limit for the active window.
            X-RateLimit-Remaining:
              schema:
                type: integer
              description: Requests remaining after this request.
            X-RateLimit-Reset:
              schema:
                type: integer
              description: Unix time when the active window resets.
            Retry-After:
              schema:
                type: integer
              description: Seconds until retry when the request is rate limited.
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
        '403':
          description: Forbidden
          headers:
            X-RateLimit-Limit:
              schema:
                type: integer
              description: Request limit for the active window.
            X-RateLimit-Remaining:
              schema:
                type: integer
              description: Requests remaining after this request.
            X-RateLimit-Reset:
              schema:
                type: integer
              description: Unix time when the active window resets.
            Retry-After:
              schema:
                type: integer
              description: Seconds until retry when the request is rate limited.
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
        '404':
          description: Not found
          headers:
            X-RateLimit-Limit:
              schema:
                type: integer
              description: Request limit for the active window.
            X-RateLimit-Remaining:
              schema:
                type: integer
              description: Requests remaining after this request.
            X-RateLimit-Reset:
              schema:
                type: integer
              description: Unix time when the active window resets.
            Retry-After:
              schema:
                type: integer
              description: Seconds until retry when the request is rate limited.
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
        '422':
          description: Invalid params
          headers:
            X-RateLimit-Limit:
              schema:
                type: integer
              description: Request limit for the active window.
            X-RateLimit-Remaining:
              schema:
                type: integer
              description: Requests remaining after this request.
            X-RateLimit-Reset:
              schema:
                type: integer
              description: Unix time when the active window resets.
            Retry-After:
              schema:
                type: integer
              description: Seconds until retry when the request is rate limited.
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
  "/api/v1/sites/{slug}/keywords":
    get:
      operationId: listSiteKeywords
      tags:
      - Sites
      summary: List keyword opportunities
      description: Returns keyword opportunities for a site. Near-duplicate phrasings
        of one search intent are collapsed under a canonical row, so the list is one
        entry per intent rather than one per phrasing; `variant_count` and `combined_volume`
        report what each entry stands for. Dismissed opportunities are excluded.
      security:
      - bearerAuth: []
      parameters:
      - name: slug
        in: path
        required: true
        schema:
          type: string
        description: Site slug.
      - name: source
        in: query
        required: false
        schema:
          type: string
          enum:
          - first_party
          - competitor_gap
          - user_seeded
          - seed_expansion
          - ai_overview_gap
          - backlink_gap
          - people_also_ask
          - modifier_expansion
          - unknown
        description: Keyword discovery source filter.
      - name: type
        in: query
        required: false
        schema:
          type: string
          enum:
          - quick_win
          - striking_distance
          - content_gap
          - eeat_gap
          - link_magnet
        description: Opportunity type filter.
      - name: priority
        in: query
        required: false
        schema:
          type: integer
          minimum: 1
          maximum: 3
        description: Priority filter.
      - name: sort
        in: query
        required: false
        schema:
          type: string
          enum:
          - volume
          - opportunity
        description: Sort order.
      - name: qualified
        in: query
        required: false
        schema:
          type: boolean
        description: When true, return planning_pool.not_flagged (ambiguous excluded).
          Default remains planner_visible.
      - name: geo
        in: query
        required: false
        schema:
          type: string
          enum:
          - comparison
          - how_to
          - definition
          - list
        description: 'GEO query-shape filter. Exclusive cascade: comparison, how_to,
          definition, list.'
      - name: page
        in: query
        required: false
        schema:
          type: integer
          minimum: 1
        description: Page number.
      - name: per_page
        in: query
        required: false
        schema:
          type: integer
          minimum: 1
          maximum: 100
        description: Items per page; defaults to 25.
      responses:
        '200':
          description: ok
          headers:
            X-RateLimit-Limit:
              schema:
                type: integer
              description: Request limit for the active window.
            X-RateLimit-Remaining:
              schema:
                type: integer
              description: Requests remaining after this request.
            X-RateLimit-Reset:
              schema:
                type: integer
              description: Unix time when the active window resets.
            Retry-After:
              schema:
                type: integer
              description: Seconds until retry when the request is rate limited.
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/KeywordListResponse"
        '401':
          description: Unauthorized
          headers:
            X-RateLimit-Limit:
              schema:
                type: integer
              description: Request limit for the active window.
            X-RateLimit-Remaining:
              schema:
                type: integer
              description: Requests remaining after this request.
            X-RateLimit-Reset:
              schema:
                type: integer
              description: Unix time when the active window resets.
            Retry-After:
              schema:
                type: integer
              description: Seconds until retry when the request is rate limited.
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
        '403':
          description: Forbidden
          headers:
            X-RateLimit-Limit:
              schema:
                type: integer
              description: Request limit for the active window.
            X-RateLimit-Remaining:
              schema:
                type: integer
              description: Requests remaining after this request.
            X-RateLimit-Reset:
              schema:
                type: integer
              description: Unix time when the active window resets.
            Retry-After:
              schema:
                type: integer
              description: Seconds until retry when the request is rate limited.
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
        '404':
          description: Not found
          headers:
            X-RateLimit-Limit:
              schema:
                type: integer
              description: Request limit for the active window.
            X-RateLimit-Remaining:
              schema:
                type: integer
              description: Requests remaining after this request.
            X-RateLimit-Reset:
              schema:
                type: integer
              description: Unix time when the active window resets.
            Retry-After:
              schema:
                type: integer
              description: Seconds until retry when the request is rate limited.
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
  "/api/v1/sites/{slug}/competitors":
    get:
      operationId: listSiteCompetitors
      tags:
      - Sites
      summary: List competitors
      description: Returns known competitors ordered by overlap descending.
      security:
      - bearerAuth: []
      parameters:
      - name: slug
        in: path
        required: true
        schema:
          type: string
        description: Site slug.
      responses:
        '200':
          description: ok
          headers:
            X-RateLimit-Limit:
              schema:
                type: integer
              description: Request limit for the active window.
            X-RateLimit-Remaining:
              schema:
                type: integer
              description: Requests remaining after this request.
            X-RateLimit-Reset:
              schema:
                type: integer
              description: Unix time when the active window resets.
            Retry-After:
              schema:
                type: integer
              description: Seconds until retry when the request is rate limited.
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/CompetitorListResponse"
        '401':
          description: Unauthorized
          headers:
            X-RateLimit-Limit:
              schema:
                type: integer
              description: Request limit for the active window.
            X-RateLimit-Remaining:
              schema:
                type: integer
              description: Requests remaining after this request.
            X-RateLimit-Reset:
              schema:
                type: integer
              description: Unix time when the active window resets.
            Retry-After:
              schema:
                type: integer
              description: Seconds until retry when the request is rate limited.
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
        '403':
          description: Forbidden
          headers:
            X-RateLimit-Limit:
              schema:
                type: integer
              description: Request limit for the active window.
            X-RateLimit-Remaining:
              schema:
                type: integer
              description: Requests remaining after this request.
            X-RateLimit-Reset:
              schema:
                type: integer
              description: Unix time when the active window resets.
            Retry-After:
              schema:
                type: integer
              description: Seconds until retry when the request is rate limited.
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
        '404':
          description: Not found
          headers:
            X-RateLimit-Limit:
              schema:
                type: integer
              description: Request limit for the active window.
            X-RateLimit-Remaining:
              schema:
                type: integer
              description: Requests remaining after this request.
            X-RateLimit-Reset:
              schema:
                type: integer
              description: Unix time when the active window resets.
            Retry-After:
              schema:
                type: integer
              description: Seconds until retry when the request is rate limited.
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
  "/api/v1/sites/{slug}/current_cycle":
    get:
      operationId: getSiteCurrentCycle
      tags:
      - Growth cycles
      summary: Get current growth cycle
      description: Returns the site's current growth cycle, the shared clock and capacity
        envelope used by both strategy and campaign lanes.
      security:
      - bearerAuth: []
      parameters:
      - name: slug
        in: path
        required: true
        schema:
          type: string
        description: Site slug.
      responses:
        '200':
          description: ok
          headers:
            X-RateLimit-Limit:
              schema:
                type: integer
              description: Request limit for the active window.
            X-RateLimit-Remaining:
              schema:
                type: integer
              description: Requests remaining after this request.
            X-RateLimit-Reset:
              schema:
                type: integer
              description: Unix time when the active window resets.
            Retry-After:
              schema:
                type: integer
              description: Seconds until retry when the request is rate limited.
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/GrowthCycleResponse"
        '401':
          description: Unauthorized
          headers:
            X-RateLimit-Limit:
              schema:
                type: integer
              description: Request limit for the active window.
            X-RateLimit-Remaining:
              schema:
                type: integer
              description: Requests remaining after this request.
            X-RateLimit-Reset:
              schema:
                type: integer
              description: Unix time when the active window resets.
            Retry-After:
              schema:
                type: integer
              description: Seconds until retry when the request is rate limited.
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
        '403':
          description: Forbidden
          headers:
            X-RateLimit-Limit:
              schema:
                type: integer
              description: Request limit for the active window.
            X-RateLimit-Remaining:
              schema:
                type: integer
              description: Requests remaining after this request.
            X-RateLimit-Reset:
              schema:
                type: integer
              description: Unix time when the active window resets.
            Retry-After:
              schema:
                type: integer
              description: Seconds until retry when the request is rate limited.
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
        '404':
          description: No current growth cycle
          headers:
            X-RateLimit-Limit:
              schema:
                type: integer
              description: Request limit for the active window.
            X-RateLimit-Remaining:
              schema:
                type: integer
              description: Requests remaining after this request.
            X-RateLimit-Reset:
              schema:
                type: integer
              description: Unix time when the active window resets.
            Retry-After:
              schema:
                type: integer
              description: Seconds until retry when the request is rate limited.
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
  "/api/v1/sites/{slug}/previous_cycle":
    get:
      operationId: getSitePreviousCycle
      tags:
      - Growth cycles
      summary: Get previous growth cycle
      description: Returns the growth cycle immediately preceding the site's current
        cycle.
      security:
      - bearerAuth: []
      parameters:
      - name: slug
        in: path
        required: true
        schema:
          type: string
        description: Site slug.
      responses:
        '200':
          description: ok
          headers:
            X-RateLimit-Limit:
              schema:
                type: integer
              description: Request limit for the active window.
            X-RateLimit-Remaining:
              schema:
                type: integer
              description: Requests remaining after this request.
            X-RateLimit-Reset:
              schema:
                type: integer
              description: Unix time when the active window resets.
            Retry-After:
              schema:
                type: integer
              description: Seconds until retry when the request is rate limited.
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/GrowthCycleResponse"
        '401':
          description: Unauthorized
          headers:
            X-RateLimit-Limit:
              schema:
                type: integer
              description: Request limit for the active window.
            X-RateLimit-Remaining:
              schema:
                type: integer
              description: Requests remaining after this request.
            X-RateLimit-Reset:
              schema:
                type: integer
              description: Unix time when the active window resets.
            Retry-After:
              schema:
                type: integer
              description: Seconds until retry when the request is rate limited.
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
        '403':
          description: Forbidden
          headers:
            X-RateLimit-Limit:
              schema:
                type: integer
              description: Request limit for the active window.
            X-RateLimit-Remaining:
              schema:
                type: integer
              description: Requests remaining after this request.
            X-RateLimit-Reset:
              schema:
                type: integer
              description: Unix time when the active window resets.
            Retry-After:
              schema:
                type: integer
              description: Seconds until retry when the request is rate limited.
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
        '404':
          description: No previous growth cycle
          headers:
            X-RateLimit-Limit:
              schema:
                type: integer
              description: Request limit for the active window.
            X-RateLimit-Remaining:
              schema:
                type: integer
              description: Requests remaining after this request.
            X-RateLimit-Reset:
              schema:
                type: integer
              description: Unix time when the active window resets.
            Retry-After:
              schema:
                type: integer
              description: Seconds until retry when the request is rate limited.
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
  "/api/v1/sites/{slug}/campaigns":
    get:
      operationId: listSiteCampaigns
      tags:
      - Campaigns
      summary: List campaigns
      description: Returns a paginated index of the site's campaigns with summary
        fields and raw work-item planning-status counts. Requires the `campaigns`
        plan feature (Growth/Admin).
      security:
      - bearerAuth: []
      parameters:
      - name: slug
        in: path
        required: true
        schema:
          type: string
        description: Site slug.
      - name: page
        in: query
        required: false
        schema:
          type: integer
          minimum: 1
        description: Page number.
      - name: per_page
        in: query
        required: false
        schema:
          type: integer
          minimum: 1
          maximum: 100
        description: Items per page; defaults to 25.
      responses:
        '200':
          description: ok
          headers:
            X-RateLimit-Limit:
              schema:
                type: integer
              description: Request limit for the active window.
            X-RateLimit-Remaining:
              schema:
                type: integer
              description: Requests remaining after this request.
            X-RateLimit-Reset:
              schema:
                type: integer
              description: Unix time when the active window resets.
            Retry-After:
              schema:
                type: integer
              description: Seconds until retry when the request is rate limited.
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/CampaignsIndexResponse"
        '401':
          description: Unauthorized
          headers:
            X-RateLimit-Limit:
              schema:
                type: integer
              description: Request limit for the active window.
            X-RateLimit-Remaining:
              schema:
                type: integer
              description: Requests remaining after this request.
            X-RateLimit-Reset:
              schema:
                type: integer
              description: Unix time when the active window resets.
            Retry-After:
              schema:
                type: integer
              description: Seconds until retry when the request is rate limited.
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
        '403':
          description: Forbidden
          headers:
            X-RateLimit-Limit:
              schema:
                type: integer
              description: Request limit for the active window.
            X-RateLimit-Remaining:
              schema:
                type: integer
              description: Requests remaining after this request.
            X-RateLimit-Reset:
              schema:
                type: integer
              description: Unix time when the active window resets.
            Retry-After:
              schema:
                type: integer
              description: Seconds until retry when the request is rate limited.
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
        '404':
          description: Not found
          headers:
            X-RateLimit-Limit:
              schema:
                type: integer
              description: Request limit for the active window.
            X-RateLimit-Remaining:
              schema:
                type: integer
              description: Requests remaining after this request.
            X-RateLimit-Reset:
              schema:
                type: integer
              description: Unix time when the active window resets.
            Retry-After:
              schema:
                type: integer
              description: Seconds until retry when the request is rate limited.
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
  "/api/v1/sites/{slug}/campaigns/{campaign_uid}":
    get:
      operationId: getSiteCampaign
      tags:
      - Campaigns
      summary: Get campaign detail
      description: 'Returns full campaign fields (positioning + hypothesis block:
        audience, opportunity, intended_behavior, evidence_basis, hypothesis, baseline)
        plus every work item with its planning/hold/lock state and projected execution
        state. Campaigns are addressed by uid, matching the SiteAudit/ContentStrategy
        child-resource convention. Requires the `campaigns` plan feature (Growth/Admin).'
      security:
      - bearerAuth: []
      parameters:
      - name: slug
        in: path
        required: true
        schema:
          type: string
        description: Site slug.
      - name: campaign_uid
        in: path
        required: true
        schema:
          type: string
        description: Campaign UID returned by the campaigns index endpoint.
      responses:
        '200':
          description: ok
          headers:
            X-RateLimit-Limit:
              schema:
                type: integer
              description: Request limit for the active window.
            X-RateLimit-Remaining:
              schema:
                type: integer
              description: Requests remaining after this request.
            X-RateLimit-Reset:
              schema:
                type: integer
              description: Unix time when the active window resets.
            Retry-After:
              schema:
                type: integer
              description: Seconds until retry when the request is rate limited.
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/CampaignDetailResponse"
        '401':
          description: Unauthorized
          headers:
            X-RateLimit-Limit:
              schema:
                type: integer
              description: Request limit for the active window.
            X-RateLimit-Remaining:
              schema:
                type: integer
              description: Requests remaining after this request.
            X-RateLimit-Reset:
              schema:
                type: integer
              description: Unix time when the active window resets.
            Retry-After:
              schema:
                type: integer
              description: Seconds until retry when the request is rate limited.
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
        '403':
          description: Forbidden
          headers:
            X-RateLimit-Limit:
              schema:
                type: integer
              description: Request limit for the active window.
            X-RateLimit-Remaining:
              schema:
                type: integer
              description: Requests remaining after this request.
            X-RateLimit-Reset:
              schema:
                type: integer
              description: Unix time when the active window resets.
            Retry-After:
              schema:
                type: integer
              description: Seconds until retry when the request is rate limited.
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
        '404':
          description: Not found
          headers:
            X-RateLimit-Limit:
              schema:
                type: integer
              description: Request limit for the active window.
            X-RateLimit-Remaining:
              schema:
                type: integer
              description: Requests remaining after this request.
            X-RateLimit-Reset:
              schema:
                type: integer
              description: Unix time when the active window resets.
            Retry-After:
              schema:
                type: integer
              description: Seconds until retry when the request is rate limited.
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
  "/api/v1/sites/{slug}/campaigns/{campaign_uid}/mission_control":
    get:
      operationId: getSiteCampaignMissionControl
      tags:
      - Campaigns
      summary: Get campaign mission control snapshot
      description: 'Returns the campaign''s mission-control read model: the single
        live gate (blocked item, pending review, or missing approved strategy — never
        more than one, never persisted), the waiting-on-you review queue, per-execution-state
        counts, the full annotated item list with resolved owner, `at_risk` — Campaigns::AtRiskPolicy''s
        findings (runway/overdue_artifact/held/unresolvable_near_end reasons, DEFAULT_RUNWAY_DAYS=2),
        `rules` — every campaign rule''s current enforced state, and `re_review_required`
        — Campaigns::ReReviewProjection''s live sweep of content artifacts whose most
        recent review snapshot predates a deterministic/reviewer rule''s current version
        (slice 7 task 2; V1 does not auto-enqueue re-reviews, this list is surfaced
        for an operator/planner to act on). All computed fresh on every call, never
        persisted. Requires the `campaigns` plan feature (Growth/Admin).'
      security:
      - bearerAuth: []
      parameters:
      - name: slug
        in: path
        required: true
        schema:
          type: string
        description: Site slug.
      - name: campaign_uid
        in: path
        required: true
        schema:
          type: string
        description: Campaign UID returned by the campaigns index endpoint.
      responses:
        '200':
          description: ok
          headers:
            X-RateLimit-Limit:
              schema:
                type: integer
              description: Request limit for the active window.
            X-RateLimit-Remaining:
              schema:
                type: integer
              description: Requests remaining after this request.
            X-RateLimit-Reset:
              schema:
                type: integer
              description: Unix time when the active window resets.
            Retry-After:
              schema:
                type: integer
              description: Seconds until retry when the request is rate limited.
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/CampaignMissionControlResponse"
        '401':
          description: Unauthorized
          headers:
            X-RateLimit-Limit:
              schema:
                type: integer
              description: Request limit for the active window.
            X-RateLimit-Remaining:
              schema:
                type: integer
              description: Requests remaining after this request.
            X-RateLimit-Reset:
              schema:
                type: integer
              description: Unix time when the active window resets.
            Retry-After:
              schema:
                type: integer
              description: Seconds until retry when the request is rate limited.
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
        '403':
          description: Forbidden
          headers:
            X-RateLimit-Limit:
              schema:
                type: integer
              description: Request limit for the active window.
            X-RateLimit-Remaining:
              schema:
                type: integer
              description: Requests remaining after this request.
            X-RateLimit-Reset:
              schema:
                type: integer
              description: Unix time when the active window resets.
            Retry-After:
              schema:
                type: integer
              description: Seconds until retry when the request is rate limited.
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
        '404':
          description: Not found
          headers:
            X-RateLimit-Limit:
              schema:
                type: integer
              description: Request limit for the active window.
            X-RateLimit-Remaining:
              schema:
                type: integer
              description: Requests remaining after this request.
            X-RateLimit-Reset:
              schema:
                type: integer
              description: Unix time when the active window resets.
            Retry-After:
              schema:
                type: integer
              description: Seconds until retry when the request is rate limited.
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
  "/api/v1/sites/{slug}/content/social_queue":
    get:
      operationId: listProductSocialQueue
      tags:
      - Sites
      summary: List due product-social Distribution tasks
      description: Returns publication tasks only after current package/channel approval,
        and measurement tasks only for published social evidence whose independent
        24h, 72h, or 7d checkpoint is due and uncaptured. Filtering happens before
        signed keyset pagination. Keep the returned `as_of` fixed by following `next_cursor`;
        execution remains owned by Distribution and must be idempotent because mutable
        approvals or record-backs can remove tasks between reads.
      security:
      - bearerAuth: []
      parameters:
      - name: slug
        in: path
        required: true
        schema:
          type: string
        description: Site slug.
      - name: kind
        in: query
        required: true
        schema:
          type: string
          enum:
          - publication
          - measurement
      - name: as_of
        in: query
        required: false
        schema:
          type: string
          format: date-time
        description: Evaluation timestamp. Defaults to server time and is embedded
          in the cursor.
      - name: cursor
        in: query
        required: false
        schema:
          type: string
        description: Opaque signed cursor from `meta.pagination.next_cursor`.
      - name: limit
        in: query
        required: false
        schema:
          type: integer
          minimum: 1
          maximum: 100
          default: 25
      responses:
        '200':
          description: ok
          headers:
            X-RateLimit-Limit:
              schema:
                type: integer
              description: Request limit for the active window.
            X-RateLimit-Remaining:
              schema:
                type: integer
              description: Requests remaining after this request.
            X-RateLimit-Reset:
              schema:
                type: integer
              description: Unix time when the active window resets.
            Retry-After:
              schema:
                type: integer
              description: Seconds until retry when the request is rate limited.
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/SocialQueueResponse"
        '401':
          description: Unauthorized
          headers:
            X-RateLimit-Limit:
              schema:
                type: integer
              description: Request limit for the active window.
            X-RateLimit-Remaining:
              schema:
                type: integer
              description: Requests remaining after this request.
            X-RateLimit-Reset:
              schema:
                type: integer
              description: Unix time when the active window resets.
            Retry-After:
              schema:
                type: integer
              description: Seconds until retry when the request is rate limited.
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
        '403':
          description: Forbidden
          headers:
            X-RateLimit-Limit:
              schema:
                type: integer
              description: Request limit for the active window.
            X-RateLimit-Remaining:
              schema:
                type: integer
              description: Requests remaining after this request.
            X-RateLimit-Reset:
              schema:
                type: integer
              description: Unix time when the active window resets.
            Retry-After:
              schema:
                type: integer
              description: Seconds until retry when the request is rate limited.
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
        '404':
          description: Not found
          headers:
            X-RateLimit-Limit:
              schema:
                type: integer
              description: Request limit for the active window.
            X-RateLimit-Remaining:
              schema:
                type: integer
              description: Requests remaining after this request.
            X-RateLimit-Reset:
              schema:
                type: integer
              description: Unix time when the active window resets.
            Retry-After:
              schema:
                type: integer
              description: Seconds until retry when the request is rate limited.
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
        '422':
          description: Invalid params
          headers:
            X-RateLimit-Limit:
              schema:
                type: integer
              description: Request limit for the active window.
            X-RateLimit-Remaining:
              schema:
                type: integer
              description: Requests remaining after this request.
            X-RateLimit-Reset:
              schema:
                type: integer
              description: Unix time when the active window resets.
            Retry-After:
              schema:
                type: integer
              description: Seconds until retry when the request is rate limited.
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
  "/api/v1/sites/{slug}/content":
    get:
      operationId: listSiteContent
      tags:
      - Sites
      summary: List content pipeline entries
      description: Returns active content pipeline entries by default with brief and
        publishing state. Use `status=archived` to see only the rows that left the
        pipeline, or `status=all` for explicit inventory analysis that includes them
        alongside active rows. Every entry carries `archived_at`/`archived_by`/`archived_reason`/`pre_archive_status`,
        so an archival is attributable rather than anonymous.
      security:
      - bearerAuth: []
      parameters:
      - name: slug
        in: path
        required: true
        schema:
          type: string
        description: Site slug.
      - name: status
        in: query
        required: false
        schema:
          type: string
          enum:
          - active
          - scheduled
          - draft
          - published
          - archived
          - all
          default: active
        description: "`active` (default) excludes archived rows and rows triaged to
          `archive`/`delete_candidate`. `archived` returns exactly that excluded set.
          `all` includes both. An unrecognised value is rejected with 422 rather than
          silently ignored."
      - name: content_type
        in: query
        required: false
        schema:
          type: string
        description: Content type filter.
      - name: triage_state
        in: query
        required: false
        schema:
          type: string
          enum:
          - triage_needed
          - current_cycle
          - unreviewed
          - keep_publish
          - regenerate
          - repurpose
          - archive
          - delete_candidate
        description: Optional triage filter. `triage_needed` matches the effective
          stale/unreviewed queue, `current_cycle` matches automation-ready cycle rows,
          and explicit triage values match `effective_triage_state` (so `unreviewed`
          rows may still have `triage_state=null`). Matching semantics are echoed
          in `meta.filters.triage_state`.
      - name: from
        in: query
        required: false
        schema:
          type: string
          format: date
        description: Inclusive scheduled date lower bound.
      - name: to
        in: query
        required: false
        schema:
          type: string
          format: date
        description: Inclusive scheduled date upper bound.
      - name: page
        in: query
        required: false
        schema:
          type: integer
          minimum: 1
        description: Page number.
      - name: per_page
        in: query
        required: false
        schema:
          type: integer
          minimum: 1
          maximum: 100
        description: Items per page; defaults to 25.
      responses:
        '200':
          description: ok
          headers:
            X-RateLimit-Limit:
              schema:
                type: integer
              description: Request limit for the active window.
            X-RateLimit-Remaining:
              schema:
                type: integer
              description: Requests remaining after this request.
            X-RateLimit-Reset:
              schema:
                type: integer
              description: Unix time when the active window resets.
            Retry-After:
              schema:
                type: integer
              description: Seconds until retry when the request is rate limited.
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ContentListResponse"
        '401':
          description: Unauthorized
          headers:
            X-RateLimit-Limit:
              schema:
                type: integer
              description: Request limit for the active window.
            X-RateLimit-Remaining:
              schema:
                type: integer
              description: Requests remaining after this request.
            X-RateLimit-Reset:
              schema:
                type: integer
              description: Unix time when the active window resets.
            Retry-After:
              schema:
                type: integer
              description: Seconds until retry when the request is rate limited.
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
        '403':
          description: Forbidden
          headers:
            X-RateLimit-Limit:
              schema:
                type: integer
              description: Request limit for the active window.
            X-RateLimit-Remaining:
              schema:
                type: integer
              description: Requests remaining after this request.
            X-RateLimit-Reset:
              schema:
                type: integer
              description: Unix time when the active window resets.
            Retry-After:
              schema:
                type: integer
              description: Seconds until retry when the request is rate limited.
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
        '404':
          description: Not found
          headers:
            X-RateLimit-Limit:
              schema:
                type: integer
              description: Request limit for the active window.
            X-RateLimit-Remaining:
              schema:
                type: integer
              description: Requests remaining after this request.
            X-RateLimit-Reset:
              schema:
                type: integer
              description: Unix time when the active window resets.
            Retry-After:
              schema:
                type: integer
              description: Seconds until retry when the request is rate limited.
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
  "/api/v1/sites/{slug}/review_queue":
    get:
      operationId: listSiteReviewQueue
      tags:
      - Sites
      summary: List review queue items
      description: 'Returns the site''s active review queue items so operators can
        inspect each reviewable before approving, requesting changes, or rejecting.
        A `strategy_approval` item includes its complete persisted ContentStrategy
        review content in `strategy`; the field is null for other review types. Each
        item''s `campaign_human_rules` lists the applicable human-mode campaign rules
        (rule_key, current version number, body) for campaign-linked content — empty
        for non-campaign content (slice 7 task 4, design §3 policy 5: human rules
        map to review-queue gates, not a new review type). `review_type: "campaign_asset_approval"`
        items (slice 9 tasks 2-3, design §3 `campaign_asset_fulfillments`) are opened
        whenever a CampaignAssetFulfillment is selected against an as-yet-unapproved
        MediaAsset (`Campaigns::SelectFulfillment` → `ReviewQueueItem.sync_for!`)
        — for these, `reviewable.type` is `"CampaignAssetFulfillment"`, `reviewable.status`
        is the fulfillment''s own status (`candidate | selected | superseded | rejected`,
        not the MediaAsset''s `approval_status`), and `reviewable.entry_uid`/`reviewable.title`
        are always null (a fulfillment has neither). Slice-10 task 1 adds `campaign_asset`
        (present only for `campaign_asset_approval` items, null otherwise) so a review-canvas
        card never has to make a second round trip to the campaign detail endpoint
        just to render itself: `requirement` (uid/asset_type/stage), `work_item` (uid/title),
        `campaign` (uid/key), and `media_asset` (uid/kind/source/usage_context/alt_text/source_url
        — cheaply-available columns only, never the ActiveStorage file/blob). An asset
        already approved by the time it''s selected never opens an item at all (skips
        straight to fulfilled).'
      security:
      - bearerAuth: []
      parameters:
      - name: slug
        in: path
        required: true
        schema:
          type: string
        description: Site slug.
      responses:
        '200':
          description: ok
          headers:
            X-RateLimit-Limit:
              schema:
                type: integer
              description: Request limit for the active window.
            X-RateLimit-Remaining:
              schema:
                type: integer
              description: Requests remaining after this request.
            X-RateLimit-Reset:
              schema:
                type: integer
              description: Unix time when the active window resets.
            Retry-After:
              schema:
                type: integer
              description: Seconds until retry when the request is rate limited.
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ReviewQueueListResponse"
        '401':
          description: Unauthorized
          headers:
            X-RateLimit-Limit:
              schema:
                type: integer
              description: Request limit for the active window.
            X-RateLimit-Remaining:
              schema:
                type: integer
              description: Requests remaining after this request.
            X-RateLimit-Reset:
              schema:
                type: integer
              description: Unix time when the active window resets.
            Retry-After:
              schema:
                type: integer
              description: Seconds until retry when the request is rate limited.
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
        '403':
          description: Forbidden
          headers:
            X-RateLimit-Limit:
              schema:
                type: integer
              description: Request limit for the active window.
            X-RateLimit-Remaining:
              schema:
                type: integer
              description: Requests remaining after this request.
            X-RateLimit-Reset:
              schema:
                type: integer
              description: Unix time when the active window resets.
            Retry-After:
              schema:
                type: integer
              description: Seconds until retry when the request is rate limited.
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
        '404':
          description: Not found
          headers:
            X-RateLimit-Limit:
              schema:
                type: integer
              description: Request limit for the active window.
            X-RateLimit-Remaining:
              schema:
                type: integer
              description: Requests remaining after this request.
            X-RateLimit-Reset:
              schema:
                type: integer
              description: Unix time when the active window resets.
            Retry-After:
              schema:
                type: integer
              description: Seconds until retry when the request is rate limited.
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
  "/api/v1/sites/{slug}/review_queue/{review_uid}":
    get:
      operationId: getSiteReviewQueueItem
      tags:
      - Sites
      summary: Get one review queue item by UID
      description: 'Returns one review queue item by UID at ANY status — including
        `approved`, `rejected`, `cancelled`, and `superseded`, which the list endpoint
        (active items only) cannot reach. This is the attribution surface: `decision`
        names the principal that approved, rejected, or requested changes on the item
        and when, and `claim_risk_floor` reports the calendar entry''s persisted claim-risk
        floor so an operator can see what a re-armed package would inherit. Same shape
        as an item in `GET /review_queue`.'
      security:
      - bearerAuth: []
      parameters:
      - name: slug
        in: path
        required: true
        schema:
          type: string
        description: Site slug.
      - name: review_uid
        in: path
        required: true
        schema:
          type: string
        description: Review queue item UID returned by the review queue endpoint.
      responses:
        '200':
          description: ok
          headers:
            X-RateLimit-Limit:
              schema:
                type: integer
              description: Request limit for the active window.
            X-RateLimit-Remaining:
              schema:
                type: integer
              description: Requests remaining after this request.
            X-RateLimit-Reset:
              schema:
                type: integer
              description: Unix time when the active window resets.
            Retry-After:
              schema:
                type: integer
              description: Seconds until retry when the request is rate limited.
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ReviewQueueItemResponse"
        '401':
          description: Unauthorized
          headers:
            X-RateLimit-Limit:
              schema:
                type: integer
              description: Request limit for the active window.
            X-RateLimit-Remaining:
              schema:
                type: integer
              description: Requests remaining after this request.
            X-RateLimit-Reset:
              schema:
                type: integer
              description: Unix time when the active window resets.
            Retry-After:
              schema:
                type: integer
              description: Seconds until retry when the request is rate limited.
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
        '403':
          description: Forbidden
          headers:
            X-RateLimit-Limit:
              schema:
                type: integer
              description: Request limit for the active window.
            X-RateLimit-Remaining:
              schema:
                type: integer
              description: Requests remaining after this request.
            X-RateLimit-Reset:
              schema:
                type: integer
              description: Unix time when the active window resets.
            Retry-After:
              schema:
                type: integer
              description: Seconds until retry when the request is rate limited.
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
        '404':
          description: Not found
          headers:
            X-RateLimit-Limit:
              schema:
                type: integer
              description: Request limit for the active window.
            X-RateLimit-Remaining:
              schema:
                type: integer
              description: Requests remaining after this request.
            X-RateLimit-Reset:
              schema:
                type: integer
              description: Unix time when the active window resets.
            Retry-After:
              schema:
                type: integer
              description: Seconds until retry when the request is rate limited.
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
  "/api/v1/sites/{slug}/content/{entry_uid}":
    get:
      operationId: getContentEntry
      tags:
      - Sites
      summary: Get a content entry and its current draft
      description: Returns one site-owned content entry by entry, brief, or draft
        UID. The nested brief includes its complete persisted outline, and the nullable
        nested draft contains the exact persisted current draft title and body plus
        its UID, version, status, and update timestamp.
      security:
      - bearerAuth: []
      parameters:
      - name: slug
        in: path
        required: true
        schema:
          type: string
        description: Site slug.
      - name: entry_uid
        in: path
        required: true
        schema:
          type: string
        description: Content entry UID returned by the content endpoint.
      responses:
        '200':
          description: ok
          headers:
            X-RateLimit-Limit:
              schema:
                type: integer
              description: Request limit for the active window.
            X-RateLimit-Remaining:
              schema:
                type: integer
              description: Requests remaining after this request.
            X-RateLimit-Reset:
              schema:
                type: integer
              description: Unix time when the active window resets.
            Retry-After:
              schema:
                type: integer
              description: Seconds until retry when the request is rate limited.
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ContentDetailResponse"
        '401':
          description: Unauthorized
          headers:
            X-RateLimit-Limit:
              schema:
                type: integer
              description: Request limit for the active window.
            X-RateLimit-Remaining:
              schema:
                type: integer
              description: Requests remaining after this request.
            X-RateLimit-Reset:
              schema:
                type: integer
              description: Unix time when the active window resets.
            Retry-After:
              schema:
                type: integer
              description: Seconds until retry when the request is rate limited.
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
        '403':
          description: Forbidden
          headers:
            X-RateLimit-Limit:
              schema:
                type: integer
              description: Request limit for the active window.
            X-RateLimit-Remaining:
              schema:
                type: integer
              description: Requests remaining after this request.
            X-RateLimit-Reset:
              schema:
                type: integer
              description: Unix time when the active window resets.
            Retry-After:
              schema:
                type: integer
              description: Seconds until retry when the request is rate limited.
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
        '404':
          description: Not found
          headers:
            X-RateLimit-Limit:
              schema:
                type: integer
              description: Request limit for the active window.
            X-RateLimit-Remaining:
              schema:
                type: integer
              description: Requests remaining after this request.
            X-RateLimit-Reset:
              schema:
                type: integer
              description: Unix time when the active window resets.
            Retry-After:
              schema:
                type: integer
              description: Seconds until retry when the request is rate limited.
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
    patch:
      operationId: updateContentEntry
      tags:
      - Sites
      summary: Update content entry targeting and scheduling metadata
      description: Updates the calendar entry in place while preserving its UID, brief,
        and draft. `title` is calendar/display scope and does not change the draft
        title. `target_keyword` does not change the brief target keyword; it relinks
        `keyword_opportunity_id` by normalized exact site-scoped match, returning
        null when the keyword is untracked. Campaign membership is read from `campaign_work_item_uid`
        and changed through campaign commands; the legacy `schedule_intent` write
        is retired. `content_type` is a correction-only field that must be sent by
        itself and is accepted only for unpublished entries; it records audit provenance,
        invalidates incompatible packages and distribution reviews, and returns the
        preserved draft to editorial review. Requires a `write`-scoped API key and
        at least one field. Unknown fields are rejected by name.
      security:
      - bearerAuth: []
      parameters:
      - name: slug
        in: path
        required: true
        schema:
          type: string
        description: Site slug.
      - name: entry_uid
        in: path
        required: true
        schema:
          type: string
        description: Content entry UID returned by the content endpoint.
      - name: title
        in: query
        required: false
        schema:
          type: string
        description: Calendar/display title. Does not change an attached draft title.
      - name: funnel_stage
        in: query
        required: false
        schema:
          type: string
          nullable: true
        description: Funnel stage for the calendar entry. Blank clears the value.
      - name: target_keyword
        in: query
        required: false
        schema:
          type: string
          nullable: true
        description: Target keyword for the calendar entry. Blank clears it. Does
          not change an attached brief target keyword.
      - name: owner_notes
        in: query
        required: false
        schema:
          type: string
          nullable: true
        description: Operator notes for the entry. Blank clears the value.
      - name: content_type
        in: query
        required: false
        schema:
          type: string
          enum:
          - article
          - blog_post
          - comparison
          - listicle
          - pillar_page
          - faq_page
          - glossary_definition
          - data_study
          - tutorial
          - case_study
          - product_update
          - changelog
          - linkedin_post
          - twitter_thread
          - twitter_post
        description: Corrects the type of an unpublished entry. Must be sent without
          other fields.
      responses:
        '200':
          description: ok
          headers:
            X-RateLimit-Limit:
              schema:
                type: integer
              description: Request limit for the active window.
            X-RateLimit-Remaining:
              schema:
                type: integer
              description: Requests remaining after this request.
            X-RateLimit-Reset:
              schema:
                type: integer
              description: Unix time when the active window resets.
            Retry-After:
              schema:
                type: integer
              description: Seconds until retry when the request is rate limited.
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ContentDetailResponse"
        '401':
          description: Unauthorized
          headers:
            X-RateLimit-Limit:
              schema:
                type: integer
              description: Request limit for the active window.
            X-RateLimit-Remaining:
              schema:
                type: integer
              description: Requests remaining after this request.
            X-RateLimit-Reset:
              schema:
                type: integer
              description: Unix time when the active window resets.
            Retry-After:
              schema:
                type: integer
              description: Seconds until retry when the request is rate limited.
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
        '403':
          description: Forbidden
          headers:
            X-RateLimit-Limit:
              schema:
                type: integer
              description: Request limit for the active window.
            X-RateLimit-Remaining:
              schema:
                type: integer
              description: Requests remaining after this request.
            X-RateLimit-Reset:
              schema:
                type: integer
              description: Unix time when the active window resets.
            Retry-After:
              schema:
                type: integer
              description: Seconds until retry when the request is rate limited.
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
        '404':
          description: Not found
          headers:
            X-RateLimit-Limit:
              schema:
                type: integer
              description: Request limit for the active window.
            X-RateLimit-Remaining:
              schema:
                type: integer
              description: Requests remaining after this request.
            X-RateLimit-Reset:
              schema:
                type: integer
              description: Unix time when the active window resets.
            Retry-After:
              schema:
                type: integer
              description: Seconds until retry when the request is rate limited.
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
        '409':
          description: Invalid state
          headers:
            X-RateLimit-Limit:
              schema:
                type: integer
              description: Request limit for the active window.
            X-RateLimit-Remaining:
              schema:
                type: integer
              description: Requests remaining after this request.
            X-RateLimit-Reset:
              schema:
                type: integer
              description: Unix time when the active window resets.
            Retry-After:
              schema:
                type: integer
              description: Seconds until retry when the request is rate limited.
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
        '422':
          description: Invalid params
          headers:
            X-RateLimit-Limit:
              schema:
                type: integer
              description: Request limit for the active window.
            X-RateLimit-Remaining:
              schema:
                type: integer
              description: Requests remaining after this request.
            X-RateLimit-Reset:
              schema:
                type: integer
              description: Unix time when the active window resets.
            Retry-After:
              schema:
                type: integer
              description: Seconds until retry when the request is rate limited.
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
  "/api/v1/sites/{slug}/review_queue/{review_uid}/reopen":
    post:
      operationId: reopenSiteReviewQueueItem
      tags:
      - Sites
      summary: Reopen a mistaken low-risk distribution change request
      description: Restores a current low-risk `distribution_approval` item from `changes_requested`
        to `pending` only when the packaged draft snapshot is unchanged. The action
        requires `distribute`, requires a reason, appends actor/timestamp/prior-decision/snapshot-fingerprint
        evidence to `reopen_history`, restores only the draft workflow state that
        `request_changes` changed, and makes its queued regeneration a no-op. It does
        not rebuild or mutate draft content, the package, variants, or approved cover.
        Medium/high-risk and inherited claim-gate veto items cannot use this recovery
        contract.
      security:
      - bearerAuth: []
      parameters:
      - name: slug
        in: path
        required: true
        schema:
          type: string
        description: Site slug.
      - name: review_uid
        in: path
        required: true
        schema:
          type: string
        description: Review queue item UID returned by the review queue endpoint.
      - name: body
        in: body
        required: true
        schema:
          type: object
          required:
          - reason
          properties:
            reason:
              type: string
      responses:
        '200':
          description: ok
          headers:
            X-RateLimit-Limit:
              schema:
                type: integer
              description: Request limit for the active window.
            X-RateLimit-Remaining:
              schema:
                type: integer
              description: Requests remaining after this request.
            X-RateLimit-Reset:
              schema:
                type: integer
              description: Unix time when the active window resets.
            Retry-After:
              schema:
                type: integer
              description: Seconds until retry when the request is rate limited.
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ReviewQueueItemResponse"
        '401':
          description: Unauthorized
          headers:
            X-RateLimit-Limit:
              schema:
                type: integer
              description: Request limit for the active window.
            X-RateLimit-Remaining:
              schema:
                type: integer
              description: Requests remaining after this request.
            X-RateLimit-Reset:
              schema:
                type: integer
              description: Unix time when the active window resets.
            Retry-After:
              schema:
                type: integer
              description: Seconds until retry when the request is rate limited.
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
        '403':
          description: Forbidden
          headers:
            X-RateLimit-Limit:
              schema:
                type: integer
              description: Request limit for the active window.
            X-RateLimit-Remaining:
              schema:
                type: integer
              description: Requests remaining after this request.
            X-RateLimit-Reset:
              schema:
                type: integer
              description: Unix time when the active window resets.
            Retry-After:
              schema:
                type: integer
              description: Seconds until retry when the request is rate limited.
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
        '404':
          description: Not found
          headers:
            X-RateLimit-Limit:
              schema:
                type: integer
              description: Request limit for the active window.
            X-RateLimit-Remaining:
              schema:
                type: integer
              description: Requests remaining after this request.
            X-RateLimit-Reset:
              schema:
                type: integer
              description: Unix time when the active window resets.
            Retry-After:
              schema:
                type: integer
              description: Seconds until retry when the request is rate limited.
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
        '422':
          description: Invalid recovery state
          headers:
            X-RateLimit-Limit:
              schema:
                type: integer
              description: Request limit for the active window.
            X-RateLimit-Remaining:
              schema:
                type: integer
              description: Requests remaining after this request.
            X-RateLimit-Reset:
              schema:
                type: integer
              description: Unix time when the active window resets.
            Retry-After:
              schema:
                type: integer
              description: Seconds until retry when the request is rate limited.
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
  "/api/v1/sites/{slug}/content/{entry_uid}/package_variant":
    get:
      operationId: getPackageContentVariant
      tags:
      - Sites
      summary: Get package content variant
      description: Returns the current packaged body for one content entry channel
        so operators can inspect distribution copy before whole-package or per-channel
        approval. Channel resolution follows the same legacy social-package fallback
        rules as the approved-variant endpoint.
      security:
      - bearerAuth: []
      parameters:
      - name: slug
        in: path
        required: true
        schema:
          type: string
        description: Site slug.
      - name: entry_uid
        in: path
        required: true
        schema:
          type: string
        description: Content calendar entry UID.
      - name: channel
        in: query
        required: true
        schema:
          type: string
        description: Target distribution channel, such as linkedin_post, linkedin,
          twitter_post, x, social, or blog.
      responses:
        '200':
          description: ok
          headers:
            X-RateLimit-Limit:
              schema:
                type: integer
              description: Request limit for the active window.
            X-RateLimit-Remaining:
              schema:
                type: integer
              description: Requests remaining after this request.
            X-RateLimit-Reset:
              schema:
                type: integer
              description: Unix time when the active window resets.
            Retry-After:
              schema:
                type: integer
              description: Seconds until retry when the request is rate limited.
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/PackageContentVariantResponse"
        '401':
          description: Unauthorized
          headers:
            X-RateLimit-Limit:
              schema:
                type: integer
              description: Request limit for the active window.
            X-RateLimit-Remaining:
              schema:
                type: integer
              description: Requests remaining after this request.
            X-RateLimit-Reset:
              schema:
                type: integer
              description: Unix time when the active window resets.
            Retry-After:
              schema:
                type: integer
              description: Seconds until retry when the request is rate limited.
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
        '403':
          description: Forbidden
          headers:
            X-RateLimit-Limit:
              schema:
                type: integer
              description: Request limit for the active window.
            X-RateLimit-Remaining:
              schema:
                type: integer
              description: Requests remaining after this request.
            X-RateLimit-Reset:
              schema:
                type: integer
              description: Unix time when the active window resets.
            Retry-After:
              schema:
                type: integer
              description: Seconds until retry when the request is rate limited.
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
        '404':
          description: Not found
          headers:
            X-RateLimit-Limit:
              schema:
                type: integer
              description: Request limit for the active window.
            X-RateLimit-Remaining:
              schema:
                type: integer
              description: Requests remaining after this request.
            X-RateLimit-Reset:
              schema:
                type: integer
              description: Unix time when the active window resets.
            Retry-After:
              schema:
                type: integer
              description: Seconds until retry when the request is rate limited.
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
        '409':
          description: Invalid state
          headers:
            X-RateLimit-Limit:
              schema:
                type: integer
              description: Request limit for the active window.
            X-RateLimit-Remaining:
              schema:
                type: integer
              description: Requests remaining after this request.
            X-RateLimit-Reset:
              schema:
                type: integer
              description: Unix time when the active window resets.
            Retry-After:
              schema:
                type: integer
              description: Seconds until retry when the request is rate limited.
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
        '422':
          description: Invalid params
          headers:
            X-RateLimit-Limit:
              schema:
                type: integer
              description: Request limit for the active window.
            X-RateLimit-Remaining:
              schema:
                type: integer
              description: Requests remaining after this request.
            X-RateLimit-Reset:
              schema:
                type: integer
              description: Unix time when the active window resets.
            Retry-After:
              schema:
                type: integer
              description: Seconds until retry when the request is rate limited.
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
  "/api/v1/sites/{slug}/content/{entry_uid}/package":
    post:
      operationId: rejectContentPackageCreate
      tags:
      - Sites
      summary: Reject POST as a package create verb
      description: 'POST /package is not a create. Prepare a package with POST /api/v1/sites/{slug}/content/{entry_uid}/publish_draft
        (distribute scope). PATCH this path to update an existing package. Authenticated
        POST returns JSON ErrorResponse 405 with Allow: PATCH rather than an HTML
        marketing 404.'
      security:
      - bearerAuth: []
      parameters:
      - name: slug
        in: path
        required: true
        schema:
          type: string
        description: Site slug.
      - name: entry_uid
        in: path
        required: true
        schema:
          type: string
        description: Content entry UID returned by the content endpoint.
      responses:
        '401':
          description: Unauthorized
          headers:
            X-RateLimit-Limit:
              schema:
                type: integer
              description: Request limit for the active window.
            X-RateLimit-Remaining:
              schema:
                type: integer
              description: Requests remaining after this request.
            X-RateLimit-Reset:
              schema:
                type: integer
              description: Unix time when the active window resets.
            Retry-After:
              schema:
                type: integer
              description: Seconds until retry when the request is rate limited.
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
        '403':
          description: Forbidden
          headers:
            X-RateLimit-Limit:
              schema:
                type: integer
              description: Request limit for the active window.
            X-RateLimit-Remaining:
              schema:
                type: integer
              description: Requests remaining after this request.
            X-RateLimit-Reset:
              schema:
                type: integer
              description: Unix time when the active window resets.
            Retry-After:
              schema:
                type: integer
              description: Seconds until retry when the request is rate limited.
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
        '404':
          description: Not found
          headers:
            X-RateLimit-Limit:
              schema:
                type: integer
              description: Request limit for the active window.
            X-RateLimit-Remaining:
              schema:
                type: integer
              description: Requests remaining after this request.
            X-RateLimit-Reset:
              schema:
                type: integer
              description: Unix time when the active window resets.
            Retry-After:
              schema:
                type: integer
              description: Seconds until retry when the request is rate limited.
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
        '405':
          description: Method not allowed
          headers:
            X-RateLimit-Limit:
              schema:
                type: integer
              description: Request limit for the active window.
            X-RateLimit-Remaining:
              schema:
                type: integer
              description: Requests remaining after this request.
            X-RateLimit-Reset:
              schema:
                type: integer
              description: Unix time when the active window resets.
            Retry-After:
              schema:
                type: integer
              description: Seconds until retry when the request is rate limited.
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
    patch:
      operationId: updateContentPackage
      tags:
      - Sites
      summary: Update or refresh a content package
      description: Updates package governance or syncs revised social copy back to
        the draft before rebuilding the package. `sync_body` carries the revised body
        text; the ambiguous `body` field is rejected. POST is not a create verb; use
        publish_draft. Requires a `distribute`-scoped API key.
      security:
      - bearerAuth: []
      parameters:
      - name: slug
        in: path
        required: true
        schema:
          type: string
        description: Site slug.
      - name: entry_uid
        in: path
        required: true
        schema:
          type: string
        description: Content entry UID returned by the content endpoint.
      responses:
        '200':
          description: ok
          headers:
            X-RateLimit-Limit:
              schema:
                type: integer
              description: Request limit for the active window.
            X-RateLimit-Remaining:
              schema:
                type: integer
              description: Requests remaining after this request.
            X-RateLimit-Reset:
              schema:
                type: integer
              description: Unix time when the active window resets.
            Retry-After:
              schema:
                type: integer
              description: Seconds until retry when the request is rate limited.
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/WorkflowMutationResponse"
        '401':
          description: Unauthorized
          headers:
            X-RateLimit-Limit:
              schema:
                type: integer
              description: Request limit for the active window.
            X-RateLimit-Remaining:
              schema:
                type: integer
              description: Requests remaining after this request.
            X-RateLimit-Reset:
              schema:
                type: integer
              description: Unix time when the active window resets.
            Retry-After:
              schema:
                type: integer
              description: Seconds until retry when the request is rate limited.
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
        '403':
          description: Forbidden
          headers:
            X-RateLimit-Limit:
              schema:
                type: integer
              description: Request limit for the active window.
            X-RateLimit-Remaining:
              schema:
                type: integer
              description: Requests remaining after this request.
            X-RateLimit-Reset:
              schema:
                type: integer
              description: Unix time when the active window resets.
            Retry-After:
              schema:
                type: integer
              description: Seconds until retry when the request is rate limited.
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
        '404':
          description: Not found
          headers:
            X-RateLimit-Limit:
              schema:
                type: integer
              description: Request limit for the active window.
            X-RateLimit-Remaining:
              schema:
                type: integer
              description: Requests remaining after this request.
            X-RateLimit-Reset:
              schema:
                type: integer
              description: Unix time when the active window resets.
            Retry-After:
              schema:
                type: integer
              description: Seconds until retry when the request is rate limited.
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
        '409':
          description: Invalid state
          headers:
            X-RateLimit-Limit:
              schema:
                type: integer
              description: Request limit for the active window.
            X-RateLimit-Remaining:
              schema:
                type: integer
              description: Requests remaining after this request.
            X-RateLimit-Reset:
              schema:
                type: integer
              description: Unix time when the active window resets.
            Retry-After:
              schema:
                type: integer
              description: Seconds until retry when the request is rate limited.
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
        '422':
          description: Invalid params
          headers:
            X-RateLimit-Limit:
              schema:
                type: integer
              description: Request limit for the active window.
            X-RateLimit-Remaining:
              schema:
                type: integer
              description: Requests remaining after this request.
            X-RateLimit-Reset:
              schema:
                type: integer
              description: Unix time when the active window resets.
            Retry-After:
              schema:
                type: integer
              description: Seconds until retry when the request is rate limited.
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
      requestBody:
        required: false
        content:
          application/json:
            schema:
              type: object
              additionalProperties: false
              properties:
                claim_risk_level:
                  type: string
                claim_evidence:
                  type: string
                refresh_package:
                  type: boolean
                channel:
                  type: string
                  description: Social channel whose packaged variant is being revised.
                sync_title:
                  type: string
                  description: Optional revised social title.
                sync_body:
                  type: string
                  minLength: 50
                  description: Revised social body text to write back to the draft
                    before rebuilding the package.
                cover_image:
                  type: object
                  additionalProperties: true
  "/api/v1/sites/{slug}/content/{entry_uid}/approved_variant":
    get:
      operationId: getApprovedContentVariant
      tags:
      - Sites
      summary: Get approved content variant
      description: Returns the exact approved postable body for a packaged content
        entry channel. `linkedin_post` and `linkedin` resolve to the exact LinkedIn/manual-post
        snapshot for social packages, while `twitter_post`, `twitter`, and `x` resolve
        to the stored social variant when no channel-specific variant exists. Already-published
        rows whose approval state later drifted return a proof-only `already_published_content_variant`
        response instead of exposing current package copy.
      security:
      - bearerAuth: []
      parameters:
      - name: slug
        in: path
        required: true
        schema:
          type: string
        description: Site slug.
      - name: entry_uid
        in: path
        required: true
        schema:
          type: string
        description: Content calendar entry UID.
      - name: channel
        in: query
        required: true
        schema:
          type: string
        description: Target distribution channel, such as linkedin_post, linkedin,
          twitter_post, x, social, or blog.
      responses:
        '200':
          description: ok
          headers:
            X-RateLimit-Limit:
              schema:
                type: integer
              description: Request limit for the active window.
            X-RateLimit-Remaining:
              schema:
                type: integer
              description: Requests remaining after this request.
            X-RateLimit-Reset:
              schema:
                type: integer
              description: Unix time when the active window resets.
            Retry-After:
              schema:
                type: integer
              description: Seconds until retry when the request is rate limited.
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ApprovedContentVariantResponse"
        '401':
          description: Unauthorized
          headers:
            X-RateLimit-Limit:
              schema:
                type: integer
              description: Request limit for the active window.
            X-RateLimit-Remaining:
              schema:
                type: integer
              description: Requests remaining after this request.
            X-RateLimit-Reset:
              schema:
                type: integer
              description: Unix time when the active window resets.
            Retry-After:
              schema:
                type: integer
              description: Seconds until retry when the request is rate limited.
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
        '403':
          description: Forbidden
          headers:
            X-RateLimit-Limit:
              schema:
                type: integer
              description: Request limit for the active window.
            X-RateLimit-Remaining:
              schema:
                type: integer
              description: Requests remaining after this request.
            X-RateLimit-Reset:
              schema:
                type: integer
              description: Unix time when the active window resets.
            Retry-After:
              schema:
                type: integer
              description: Seconds until retry when the request is rate limited.
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
        '404':
          description: Not found
          headers:
            X-RateLimit-Limit:
              schema:
                type: integer
              description: Request limit for the active window.
            X-RateLimit-Remaining:
              schema:
                type: integer
              description: Requests remaining after this request.
            X-RateLimit-Reset:
              schema:
                type: integer
              description: Unix time when the active window resets.
            Retry-After:
              schema:
                type: integer
              description: Seconds until retry when the request is rate limited.
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
        '409':
          description: Approval required
          headers:
            X-RateLimit-Limit:
              schema:
                type: integer
              description: Request limit for the active window.
            X-RateLimit-Remaining:
              schema:
                type: integer
              description: Requests remaining after this request.
            X-RateLimit-Reset:
              schema:
                type: integer
              description: Unix time when the active window resets.
            Retry-After:
              schema:
                type: integer
              description: Seconds until retry when the request is rate limited.
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
        '422':
          description: Invalid params
          headers:
            X-RateLimit-Limit:
              schema:
                type: integer
              description: Request limit for the active window.
            X-RateLimit-Remaining:
              schema:
                type: integer
              description: Requests remaining after this request.
            X-RateLimit-Reset:
              schema:
                type: integer
              description: Unix time when the active window resets.
            Retry-After:
              schema:
                type: integer
              description: Seconds until retry when the request is rate limited.
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
  "/api/v1/sites/{slug}/content/{entry_uid}/rearm_social_package":
    post:
      operationId: rearmQueuedSocialPackage
      tags:
      - Sites
      summary: Re-arm an unchanged queued social package
      description: Atomically recovers only the current matching approved editorial
        snapshot, restores variant and distribution fingerprints, and optionally approves
        the resolved channel. Requires an unchanged scheduled publication record and
        an existing approved distribution review. It never refreshes copy, changes
        scheduling/publication records, or reruns editorial review. Idempotent and
        requires `write` and `distribute` scopes.
      security:
      - bearerAuth: []
      parameters:
      - name: slug
        in: path
        required: true
        schema:
          type: string
        description: Site slug.
      - name: entry_uid
        in: path
        required: true
        schema:
          type: string
        description: Content entry UID returned by the content endpoint.
      - name: channel
        in: query
        required: true
        schema:
          type: string
        description: Queued social channel, such as x or linkedin.
      - name: channel_state
        in: query
        required: false
        schema:
          type: string
          enum:
          - approved
        description: When supplied, records the actual channel approval after all
          unchanged-payload gates pass.
      responses:
        '200':
          description: ok
          headers:
            X-RateLimit-Limit:
              schema:
                type: integer
              description: Request limit for the active window.
            X-RateLimit-Remaining:
              schema:
                type: integer
              description: Requests remaining after this request.
            X-RateLimit-Reset:
              schema:
                type: integer
              description: Unix time when the active window resets.
            Retry-After:
              schema:
                type: integer
              description: Seconds until retry when the request is rate limited.
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ApprovedContentVariantResponse"
        '401':
          description: Unauthorized
          headers:
            X-RateLimit-Limit:
              schema:
                type: integer
              description: Request limit for the active window.
            X-RateLimit-Remaining:
              schema:
                type: integer
              description: Requests remaining after this request.
            X-RateLimit-Reset:
              schema:
                type: integer
              description: Unix time when the active window resets.
            Retry-After:
              schema:
                type: integer
              description: Seconds until retry when the request is rate limited.
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
        '403':
          description: Forbidden
          headers:
            X-RateLimit-Limit:
              schema:
                type: integer
              description: Request limit for the active window.
            X-RateLimit-Remaining:
              schema:
                type: integer
              description: Requests remaining after this request.
            X-RateLimit-Reset:
              schema:
                type: integer
              description: Unix time when the active window resets.
            Retry-After:
              schema:
                type: integer
              description: Seconds until retry when the request is rate limited.
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
        '404':
          description: Not found
          headers:
            X-RateLimit-Limit:
              schema:
                type: integer
              description: Request limit for the active window.
            X-RateLimit-Remaining:
              schema:
                type: integer
              description: Requests remaining after this request.
            X-RateLimit-Reset:
              schema:
                type: integer
              description: Unix time when the active window resets.
            Retry-After:
              schema:
                type: integer
              description: Seconds until retry when the request is rate limited.
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
        '409':
          description: Approval, state, or payload mismatch
          headers:
            X-RateLimit-Limit:
              schema:
                type: integer
              description: Request limit for the active window.
            X-RateLimit-Remaining:
              schema:
                type: integer
              description: Requests remaining after this request.
            X-RateLimit-Reset:
              schema:
                type: integer
              description: Unix time when the active window resets.
            Retry-After:
              schema:
                type: integer
              description: Seconds until retry when the request is rate limited.
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
        '422':
          description: Invalid params
          headers:
            X-RateLimit-Limit:
              schema:
                type: integer
              description: Request limit for the active window.
            X-RateLimit-Remaining:
              schema:
                type: integer
              description: Requests remaining after this request.
            X-RateLimit-Reset:
              schema:
                type: integer
              description: Unix time when the active window resets.
            Retry-After:
              schema:
                type: integer
              description: Seconds until retry when the request is rate limited.
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
  "/api/v1/sites/{slug}/eeat":
    get:
      operationId: getSiteEeat
      tags:
      - Sites
      summary: Get E-E-A-T details
      description: Returns pillar totals and individual signal recommendations.
      security:
      - bearerAuth: []
      parameters:
      - name: slug
        in: path
        required: true
        schema:
          type: string
        description: Site slug.
      responses:
        '200':
          description: ok
          headers:
            X-RateLimit-Limit:
              schema:
                type: integer
              description: Request limit for the active window.
            X-RateLimit-Remaining:
              schema:
                type: integer
              description: Requests remaining after this request.
            X-RateLimit-Reset:
              schema:
                type: integer
              description: Unix time when the active window resets.
            Retry-After:
              schema:
                type: integer
              description: Seconds until retry when the request is rate limited.
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/EeatResponse"
        '401':
          description: Unauthorized
          headers:
            X-RateLimit-Limit:
              schema:
                type: integer
              description: Request limit for the active window.
            X-RateLimit-Remaining:
              schema:
                type: integer
              description: Requests remaining after this request.
            X-RateLimit-Reset:
              schema:
                type: integer
              description: Unix time when the active window resets.
            Retry-After:
              schema:
                type: integer
              description: Seconds until retry when the request is rate limited.
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
        '403':
          description: Forbidden
          headers:
            X-RateLimit-Limit:
              schema:
                type: integer
              description: Request limit for the active window.
            X-RateLimit-Remaining:
              schema:
                type: integer
              description: Requests remaining after this request.
            X-RateLimit-Reset:
              schema:
                type: integer
              description: Unix time when the active window resets.
            Retry-After:
              schema:
                type: integer
              description: Seconds until retry when the request is rate limited.
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
        '404':
          description: Not found
          headers:
            X-RateLimit-Limit:
              schema:
                type: integer
              description: Request limit for the active window.
            X-RateLimit-Remaining:
              schema:
                type: integer
              description: Requests remaining after this request.
            X-RateLimit-Reset:
              schema:
                type: integer
              description: Unix time when the active window resets.
            Retry-After:
              schema:
                type: integer
              description: Seconds until retry when the request is rate limited.
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
  "/api/v1/sites/{slug}/summary":
    get:
      operationId: getSiteSummary
      tags:
      - Sites
      summary: Get site summary
      description: Returns a composite summary of scores, issues, keyword movement,
        content pipeline, and the latest GSC snapshot.
      security:
      - bearerAuth: []
      parameters:
      - name: slug
        in: path
        required: true
        schema:
          type: string
        description: Site slug.
      responses:
        '200':
          description: ok
          headers:
            X-RateLimit-Limit:
              schema:
                type: integer
              description: Request limit for the active window.
            X-RateLimit-Remaining:
              schema:
                type: integer
              description: Requests remaining after this request.
            X-RateLimit-Reset:
              schema:
                type: integer
              description: Unix time when the active window resets.
            Retry-After:
              schema:
                type: integer
              description: Seconds until retry when the request is rate limited.
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/SummaryResponse"
        '401':
          description: Unauthorized
          headers:
            X-RateLimit-Limit:
              schema:
                type: integer
              description: Request limit for the active window.
            X-RateLimit-Remaining:
              schema:
                type: integer
              description: Requests remaining after this request.
            X-RateLimit-Reset:
              schema:
                type: integer
              description: Unix time when the active window resets.
            Retry-After:
              schema:
                type: integer
              description: Seconds until retry when the request is rate limited.
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
        '403':
          description: Forbidden
          headers:
            X-RateLimit-Limit:
              schema:
                type: integer
              description: Request limit for the active window.
            X-RateLimit-Remaining:
              schema:
                type: integer
              description: Requests remaining after this request.
            X-RateLimit-Reset:
              schema:
                type: integer
              description: Unix time when the active window resets.
            Retry-After:
              schema:
                type: integer
              description: Seconds until retry when the request is rate limited.
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
        '404':
          description: Not found
          headers:
            X-RateLimit-Limit:
              schema:
                type: integer
              description: Request limit for the active window.
            X-RateLimit-Remaining:
              schema:
                type: integer
              description: Requests remaining after this request.
            X-RateLimit-Reset:
              schema:
                type: integer
              description: Unix time when the active window resets.
            Retry-After:
              schema:
                type: integer
              description: Seconds until retry when the request is rate limited.
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
  "/api/v1/sites/{slug}/publication_evidence_packet":
    get:
      operationId: getSitePublicationEvidencePacket
      tags:
      - Sites
      summary: Get publication evidence packet
      description: Returns a publication-safe operator packet that preserves public-proof
        attribution, operator-only evidence, content publication states, and machine-readable
        safety flags. This is the canonical operator score source via operator_summary
        and operator_summary.score_breakdown.
      security:
      - bearerAuth: []
      parameters:
      - name: slug
        in: path
        required: true
        schema:
          type: string
        description: Site slug.
      responses:
        '200':
          description: ok
          headers:
            X-RateLimit-Limit:
              schema:
                type: integer
              description: Request limit for the active window.
            X-RateLimit-Remaining:
              schema:
                type: integer
              description: Requests remaining after this request.
            X-RateLimit-Reset:
              schema:
                type: integer
              description: Unix time when the active window resets.
            Retry-After:
              schema:
                type: integer
              description: Seconds until retry when the request is rate limited.
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/PublicationEvidencePacketResponse"
        '401':
          description: Unauthorized
          headers:
            X-RateLimit-Limit:
              schema:
                type: integer
              description: Request limit for the active window.
            X-RateLimit-Remaining:
              schema:
                type: integer
              description: Requests remaining after this request.
            X-RateLimit-Reset:
              schema:
                type: integer
              description: Unix time when the active window resets.
            Retry-After:
              schema:
                type: integer
              description: Seconds until retry when the request is rate limited.
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
        '403':
          description: Forbidden
          headers:
            X-RateLimit-Limit:
              schema:
                type: integer
              description: Request limit for the active window.
            X-RateLimit-Remaining:
              schema:
                type: integer
              description: Requests remaining after this request.
            X-RateLimit-Reset:
              schema:
                type: integer
              description: Unix time when the active window resets.
            Retry-After:
              schema:
                type: integer
              description: Seconds until retry when the request is rate limited.
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
        '404':
          description: Not found
          headers:
            X-RateLimit-Limit:
              schema:
                type: integer
              description: Request limit for the active window.
            X-RateLimit-Remaining:
              schema:
                type: integer
              description: Requests remaining after this request.
            X-RateLimit-Reset:
              schema:
                type: integer
              description: Unix time when the active window resets.
            Retry-After:
              schema:
                type: integer
              description: Seconds until retry when the request is rate limited.
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
  "/api/v1/sites/{slug}/next_actions":
    get:
      operationId: getSiteNextActions
      tags:
      - Sites
      summary: Get next best actions
      description: Returns prioritized, evidence-backed next actions for the current
        site growth cycle. Audit-issue actions are aggregated by issue_type — one
        action covers all affected pages, listed in evidence_source.pages with the
        total in evidence_source.affected_count.
      security:
      - bearerAuth: []
      parameters:
      - name: slug
        in: path
        required: true
        schema:
          type: string
        description: Site slug.
      responses:
        '200':
          description: ok
          headers:
            X-RateLimit-Limit:
              schema:
                type: integer
              description: Request limit for the active window.
            X-RateLimit-Remaining:
              schema:
                type: integer
              description: Requests remaining after this request.
            X-RateLimit-Reset:
              schema:
                type: integer
              description: Unix time when the active window resets.
            Retry-After:
              schema:
                type: integer
              description: Seconds until retry when the request is rate limited.
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/NextActionsResponse"
        '401':
          description: Unauthorized
          headers:
            X-RateLimit-Limit:
              schema:
                type: integer
              description: Request limit for the active window.
            X-RateLimit-Remaining:
              schema:
                type: integer
              description: Requests remaining after this request.
            X-RateLimit-Reset:
              schema:
                type: integer
              description: Unix time when the active window resets.
            Retry-After:
              schema:
                type: integer
              description: Seconds until retry when the request is rate limited.
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
        '403':
          description: Forbidden
          headers:
            X-RateLimit-Limit:
              schema:
                type: integer
              description: Request limit for the active window.
            X-RateLimit-Remaining:
              schema:
                type: integer
              description: Requests remaining after this request.
            X-RateLimit-Reset:
              schema:
                type: integer
              description: Unix time when the active window resets.
            Retry-After:
              schema:
                type: integer
              description: Seconds until retry when the request is rate limited.
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
        '404':
          description: Not found
          headers:
            X-RateLimit-Limit:
              schema:
                type: integer
              description: Request limit for the active window.
            X-RateLimit-Remaining:
              schema:
                type: integer
              description: Requests remaining after this request.
            X-RateLimit-Reset:
              schema:
                type: integer
              description: Unix time when the active window resets.
            Retry-After:
              schema:
                type: integer
              description: Seconds until retry when the request is rate limited.
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
  "/api/v1/sites/{slug}/changes":
    get:
      operationId: getSiteChanges
      tags:
      - Sites
      summary: Get recent site changes
      description: Returns recent issue, score, keyword, content, and GSC deltas for
        a time window.
      security:
      - bearerAuth: []
      parameters:
      - name: slug
        in: path
        required: true
        schema:
          type: string
        description: Site slug.
      - name: since
        in: query
        required: false
        schema:
          type: string
          enum:
          - 1d
          - 7d
          - 14d
          - 30d
        description: Change window. Defaults to `7d`.
      responses:
        '200':
          description: ok
          headers:
            X-RateLimit-Limit:
              schema:
                type: integer
              description: Request limit for the active window.
            X-RateLimit-Remaining:
              schema:
                type: integer
              description: Requests remaining after this request.
            X-RateLimit-Reset:
              schema:
                type: integer
              description: Unix time when the active window resets.
            Retry-After:
              schema:
                type: integer
              description: Seconds until retry when the request is rate limited.
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ChangesResponse"
        '401':
          description: Unauthorized
          headers:
            X-RateLimit-Limit:
              schema:
                type: integer
              description: Request limit for the active window.
            X-RateLimit-Remaining:
              schema:
                type: integer
              description: Requests remaining after this request.
            X-RateLimit-Reset:
              schema:
                type: integer
              description: Unix time when the active window resets.
            Retry-After:
              schema:
                type: integer
              description: Seconds until retry when the request is rate limited.
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
        '403':
          description: Forbidden
          headers:
            X-RateLimit-Limit:
              schema:
                type: integer
              description: Request limit for the active window.
            X-RateLimit-Remaining:
              schema:
                type: integer
              description: Requests remaining after this request.
            X-RateLimit-Reset:
              schema:
                type: integer
              description: Unix time when the active window resets.
            Retry-After:
              schema:
                type: integer
              description: Seconds until retry when the request is rate limited.
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
        '404':
          description: Not found
          headers:
            X-RateLimit-Limit:
              schema:
                type: integer
              description: Request limit for the active window.
            X-RateLimit-Remaining:
              schema:
                type: integer
              description: Requests remaining after this request.
            X-RateLimit-Reset:
              schema:
                type: integer
              description: Unix time when the active window resets.
            Retry-After:
              schema:
                type: integer
              description: Seconds until retry when the request is rate limited.
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
        '422':
          description: Invalid params
          headers:
            X-RateLimit-Limit:
              schema:
                type: integer
              description: Request limit for the active window.
            X-RateLimit-Remaining:
              schema:
                type: integer
              description: Requests remaining after this request.
            X-RateLimit-Reset:
              schema:
                type: integer
              description: Unix time when the active window resets.
            Retry-After:
              schema:
                type: integer
              description: Seconds until retry when the request is rate limited.
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
  "/api/v1/sites/{slug}/trigger_audit":
    post:
      operationId: triggerSiteAudit
      tags:
      - Sites
      summary: Trigger a manual audit
      description: Queues a manual site audit. Free on every plan and limited to one
        per site per hour, shared with the in-app trigger. The 202 body contains `job_id`;
        poll `/api/v1/sites/{slug}/jobs/{job_id}`. Requires a `write`-scoped API key.
      security:
      - bearerAuth: []
      parameters:
      - name: slug
        in: path
        required: true
        schema:
          type: string
        description: Site slug.
      responses:
        '202':
          description: accepted
          headers:
            X-RateLimit-Limit:
              schema:
                type: integer
              description: Request limit for the active window.
            X-RateLimit-Remaining:
              schema:
                type: integer
              description: Requests remaining after this request.
            X-RateLimit-Reset:
              schema:
                type: integer
              description: Unix time when the active window resets.
            Retry-After:
              schema:
                type: integer
              description: Seconds until retry when the request is rate limited.
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/TriggerAuditResponse"
        '401':
          description: Unauthorized
          headers:
            X-RateLimit-Limit:
              schema:
                type: integer
              description: Request limit for the active window.
            X-RateLimit-Remaining:
              schema:
                type: integer
              description: Requests remaining after this request.
            X-RateLimit-Reset:
              schema:
                type: integer
              description: Unix time when the active window resets.
            Retry-After:
              schema:
                type: integer
              description: Seconds until retry when the request is rate limited.
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
        '403':
          description: Plan limited or coming soon
          headers:
            X-RateLimit-Limit:
              schema:
                type: integer
              description: Request limit for the active window.
            X-RateLimit-Remaining:
              schema:
                type: integer
              description: Requests remaining after this request.
            X-RateLimit-Reset:
              schema:
                type: integer
              description: Unix time when the active window resets.
            Retry-After:
              schema:
                type: integer
              description: Seconds until retry when the request is rate limited.
          content:
            application/json:
              schema:
                oneOf:
                - "$ref": "#/components/schemas/PlanLimitErrorResponse"
                - "$ref": "#/components/schemas/ComingSoonErrorResponse"
        '404':
          description: Not found
          headers:
            X-RateLimit-Limit:
              schema:
                type: integer
              description: Request limit for the active window.
            X-RateLimit-Remaining:
              schema:
                type: integer
              description: Requests remaining after this request.
            X-RateLimit-Reset:
              schema:
                type: integer
              description: Unix time when the active window resets.
            Retry-After:
              schema:
                type: integer
              description: Seconds until retry when the request is rate limited.
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
        '409':
          description: Already running
          headers:
            X-RateLimit-Limit:
              schema:
                type: integer
              description: Request limit for the active window.
            X-RateLimit-Remaining:
              schema:
                type: integer
              description: Requests remaining after this request.
            X-RateLimit-Reset:
              schema:
                type: integer
              description: Unix time when the active window resets.
            Retry-After:
              schema:
                type: integer
              description: Seconds until retry when the request is rate limited.
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/AlreadyRunningErrorResponse"
        '429':
          description: Rate limited
          headers:
            X-RateLimit-Limit:
              schema:
                type: integer
              description: Request limit for the active window.
            X-RateLimit-Remaining:
              schema:
                type: integer
              description: Requests remaining after this request.
            X-RateLimit-Reset:
              schema:
                type: integer
              description: Unix time when the active window resets.
            Retry-After:
              schema:
                type: integer
              description: Seconds until retry when the request is rate limited.
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/RateLimitedErrorResponse"
  "/api/v1/sites/{slug}/strategy":
    get:
      operationId: getSiteStrategy
      tags:
      - Sites
      summary: Get current content strategy
      description: Returns the latest non-archived strategy for Starter or higher
        plans. Nullable strategy fields remain null. Approval is available only in
        the TrustGrowth app.
      security:
      - bearerAuth: []
      parameters:
      - name: slug
        in: path
        required: true
        schema:
          type: string
        description: Site slug.
      responses:
        '200':
          description: ok
          headers:
            X-RateLimit-Limit:
              schema:
                type: integer
              description: Request limit for the active window.
            X-RateLimit-Remaining:
              schema:
                type: integer
              description: Requests remaining after this request.
            X-RateLimit-Reset:
              schema:
                type: integer
              description: Unix time when the active window resets.
            Retry-After:
              schema:
                type: integer
              description: Seconds until retry when the request is rate limited.
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/StrategyResponse"
        '401':
          description: Unauthorized
          headers:
            X-RateLimit-Limit:
              schema:
                type: integer
              description: Request limit for the active window.
            X-RateLimit-Remaining:
              schema:
                type: integer
              description: Requests remaining after this request.
            X-RateLimit-Reset:
              schema:
                type: integer
              description: Unix time when the active window resets.
            Retry-After:
              schema:
                type: integer
              description: Seconds until retry when the request is rate limited.
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
        '403':
          description: Plan limited or coming soon
          headers:
            X-RateLimit-Limit:
              schema:
                type: integer
              description: Request limit for the active window.
            X-RateLimit-Remaining:
              schema:
                type: integer
              description: Requests remaining after this request.
            X-RateLimit-Reset:
              schema:
                type: integer
              description: Unix time when the active window resets.
            Retry-After:
              schema:
                type: integer
              description: Seconds until retry when the request is rate limited.
          content:
            application/json:
              schema:
                oneOf:
                - "$ref": "#/components/schemas/PlanLimitErrorResponse"
                - "$ref": "#/components/schemas/ComingSoonErrorResponse"
        '404':
          description: Strategy not found
          headers:
            X-RateLimit-Limit:
              schema:
                type: integer
              description: Request limit for the active window.
            X-RateLimit-Remaining:
              schema:
                type: integer
              description: Requests remaining after this request.
            X-RateLimit-Reset:
              schema:
                type: integer
              description: Unix time when the active window resets.
            Retry-After:
              schema:
                type: integer
              description: Seconds until retry when the request is rate limited.
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
  "/api/v1/sites/{slug}/regenerate_strategy":
    post:
      operationId: regenerateStrategy
      tags:
      - Sites
      summary: Regenerate current strategy
      description: Queues strategy generation when none exists, or regeneration for
        the current actionable strategy. Returns `409 cycle_active` when the approved
        strategy is still inside an active growth cycle. The response points to the
        in-app approval URL; approval is not public. Requires a `write`-scoped API
        key.
      security:
      - bearerAuth: []
      parameters:
      - name: slug
        in: path
        required: true
        schema:
          type: string
        description: Site slug.
      - name: feedback
        in: query
        required: false
        schema:
          type: string
        description: Optional feedback for the regenerated strategy.
      responses:
        '202':
          description: accepted
          headers:
            X-RateLimit-Limit:
              schema:
                type: integer
              description: Request limit for the active window.
            X-RateLimit-Remaining:
              schema:
                type: integer
              description: Requests remaining after this request.
            X-RateLimit-Reset:
              schema:
                type: integer
              description: Unix time when the active window resets.
            Retry-After:
              schema:
                type: integer
              description: Seconds until retry when the request is rate limited.
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/WorkflowMutationResponse"
        '402':
          description: Insufficient credits
          headers:
            X-RateLimit-Limit:
              schema:
                type: integer
              description: Request limit for the active window.
            X-RateLimit-Remaining:
              schema:
                type: integer
              description: Requests remaining after this request.
            X-RateLimit-Reset:
              schema:
                type: integer
              description: Unix time when the active window resets.
            Retry-After:
              schema:
                type: integer
              description: Seconds until retry when the request is rate limited.
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
        '401':
          description: Unauthorized
          headers:
            X-RateLimit-Limit:
              schema:
                type: integer
              description: Request limit for the active window.
            X-RateLimit-Remaining:
              schema:
                type: integer
              description: Requests remaining after this request.
            X-RateLimit-Reset:
              schema:
                type: integer
              description: Unix time when the active window resets.
            Retry-After:
              schema:
                type: integer
              description: Seconds until retry when the request is rate limited.
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
        '403':
          description: Plan limited or coming soon
          headers:
            X-RateLimit-Limit:
              schema:
                type: integer
              description: Request limit for the active window.
            X-RateLimit-Remaining:
              schema:
                type: integer
              description: Requests remaining after this request.
            X-RateLimit-Reset:
              schema:
                type: integer
              description: Unix time when the active window resets.
            Retry-After:
              schema:
                type: integer
              description: Seconds until retry when the request is rate limited.
          content:
            application/json:
              schema:
                oneOf:
                - "$ref": "#/components/schemas/PlanLimitErrorResponse"
                - "$ref": "#/components/schemas/ComingSoonErrorResponse"
        '404':
          description: Not found
          headers:
            X-RateLimit-Limit:
              schema:
                type: integer
              description: Request limit for the active window.
            X-RateLimit-Remaining:
              schema:
                type: integer
              description: Requests remaining after this request.
            X-RateLimit-Reset:
              schema:
                type: integer
              description: Unix time when the active window resets.
            Retry-After:
              schema:
                type: integer
              description: Seconds until retry when the request is rate limited.
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
        '409':
          description: Invalid state
          headers:
            X-RateLimit-Limit:
              schema:
                type: integer
              description: Request limit for the active window.
            X-RateLimit-Remaining:
              schema:
                type: integer
              description: Requests remaining after this request.
            X-RateLimit-Reset:
              schema:
                type: integer
              description: Unix time when the active window resets.
            Retry-After:
              schema:
                type: integer
              description: Seconds until retry when the request is rate limited.
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
        '429':
          description: Rate limited
          headers:
            X-RateLimit-Limit:
              schema:
                type: integer
              description: Request limit for the active window.
            X-RateLimit-Remaining:
              schema:
                type: integer
              description: Requests remaining after this request.
            X-RateLimit-Reset:
              schema:
                type: integer
              description: Unix time when the active window resets.
            Retry-After:
              schema:
                type: integer
              description: Seconds until retry when the request is rate limited.
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
  "/api/v1/sites/{slug}/content/{entry_uid}/approve_brief":
    post:
      operationId: approveContentBrief
      tags:
      - Sites
      summary: Approve content brief
      description: Approves a pending-review brief. Draft generation starts by default;
        `start_draft=false` approves without a draft job. When an approved brief has
        no draft, the default operation safely retries generation or returns the existing
        active draft-generation job. `content_intent` declares that competing with
        an existing ranking URL is deliberate, which downgrades the review's cannibalization
        blocker to a warning. Requires a `write`-scoped API key.
      security:
      - bearerAuth: []
      parameters:
      - name: slug
        in: path
        required: true
        schema:
          type: string
        description: Site slug.
      - name: entry_uid
        in: path
        required: true
        schema:
          type: string
        description: Content entry UID returned by the content endpoint.
      - name: start_draft
        in: query
        required: false
        schema:
          type: boolean
          default: true
        description: Set false to approve without starting draft generation.
      - name: content_intent
        in: query
        required: false
        schema:
          type: string
          enum:
          - refresh
          - cluster_support
        description: Declare deliberate overlap with an existing ranking URL. Omit
          unless it is deliberate.
      - name: editorial_contract
        in: query
        required: false
        schema:
          type: string
          enum:
          - no_measurement_methodology
        description: Declare the piece's rubric content type. `no_measurement_methodology`
          scopes out the experience and data-density checks a documented protocol
          cannot satisfy; fabrication gates stay armed.
      - name: series_key
        in: query
        required: false
        schema:
          type: string
        description: Declares this brief a part of a named series. Siblings sharing
          the key are excluded from the originality doctrine's corpus.
      responses:
        '202':
          description: accepted
          headers:
            X-RateLimit-Limit:
              schema:
                type: integer
              description: Request limit for the active window.
            X-RateLimit-Remaining:
              schema:
                type: integer
              description: Requests remaining after this request.
            X-RateLimit-Reset:
              schema:
                type: integer
              description: Unix time when the active window resets.
            Retry-After:
              schema:
                type: integer
              description: Seconds until retry when the request is rate limited.
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/WorkflowMutationResponse"
        '401':
          description: Unauthorized
          headers:
            X-RateLimit-Limit:
              schema:
                type: integer
              description: Request limit for the active window.
            X-RateLimit-Remaining:
              schema:
                type: integer
              description: Requests remaining after this request.
            X-RateLimit-Reset:
              schema:
                type: integer
              description: Unix time when the active window resets.
            Retry-After:
              schema:
                type: integer
              description: Seconds until retry when the request is rate limited.
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
        '403':
          description: Forbidden
          headers:
            X-RateLimit-Limit:
              schema:
                type: integer
              description: Request limit for the active window.
            X-RateLimit-Remaining:
              schema:
                type: integer
              description: Requests remaining after this request.
            X-RateLimit-Reset:
              schema:
                type: integer
              description: Unix time when the active window resets.
            Retry-After:
              schema:
                type: integer
              description: Seconds until retry when the request is rate limited.
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
        '404':
          description: Not found
          headers:
            X-RateLimit-Limit:
              schema:
                type: integer
              description: Request limit for the active window.
            X-RateLimit-Remaining:
              schema:
                type: integer
              description: Requests remaining after this request.
            X-RateLimit-Reset:
              schema:
                type: integer
              description: Unix time when the active window resets.
            Retry-After:
              schema:
                type: integer
              description: Seconds until retry when the request is rate limited.
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
        '409':
          description: Invalid state
          headers:
            X-RateLimit-Limit:
              schema:
                type: integer
              description: Request limit for the active window.
            X-RateLimit-Remaining:
              schema:
                type: integer
              description: Requests remaining after this request.
            X-RateLimit-Reset:
              schema:
                type: integer
              description: Unix time when the active window resets.
            Retry-After:
              schema:
                type: integer
              description: Seconds until retry when the request is rate limited.
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
  "/api/v1/sites/{slug}/content/{entry_uid}/regenerate_brief":
    post:
      operationId: regenerateContentBrief
      tags:
      - Sites
      summary: Regenerate content brief
      description: Revises a pending-review brief in place using required operator
        feedback while preserving entry and brief UIDs. Requires a `write`-scoped
        API key.
      security:
      - bearerAuth: []
      parameters:
      - name: slug
        in: path
        required: true
        schema:
          type: string
        description: Site slug.
      - name: entry_uid
        in: path
        required: true
        schema:
          type: string
        description: Content entry UID returned by the content endpoint.
      - name: feedback
        in: query
        required: true
        schema:
          type: string
        description: Required instructions for revising the current brief.
      responses:
        '202':
          description: accepted
          headers:
            X-RateLimit-Limit:
              schema:
                type: integer
              description: Request limit for the active window.
            X-RateLimit-Remaining:
              schema:
                type: integer
              description: Requests remaining after this request.
            X-RateLimit-Reset:
              schema:
                type: integer
              description: Unix time when the active window resets.
            Retry-After:
              schema:
                type: integer
              description: Seconds until retry when the request is rate limited.
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/WorkflowMutationResponse"
        '401':
          description: Unauthorized
          headers:
            X-RateLimit-Limit:
              schema:
                type: integer
              description: Request limit for the active window.
            X-RateLimit-Remaining:
              schema:
                type: integer
              description: Requests remaining after this request.
            X-RateLimit-Reset:
              schema:
                type: integer
              description: Unix time when the active window resets.
            Retry-After:
              schema:
                type: integer
              description: Seconds until retry when the request is rate limited.
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
        '403':
          description: Forbidden
          headers:
            X-RateLimit-Limit:
              schema:
                type: integer
              description: Request limit for the active window.
            X-RateLimit-Remaining:
              schema:
                type: integer
              description: Requests remaining after this request.
            X-RateLimit-Reset:
              schema:
                type: integer
              description: Unix time when the active window resets.
            Retry-After:
              schema:
                type: integer
              description: Seconds until retry when the request is rate limited.
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
        '404':
          description: Not found
          headers:
            X-RateLimit-Limit:
              schema:
                type: integer
              description: Request limit for the active window.
            X-RateLimit-Remaining:
              schema:
                type: integer
              description: Requests remaining after this request.
            X-RateLimit-Reset:
              schema:
                type: integer
              description: Unix time when the active window resets.
            Retry-After:
              schema:
                type: integer
              description: Seconds until retry when the request is rate limited.
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
        '409':
          description: Invalid state
          headers:
            X-RateLimit-Limit:
              schema:
                type: integer
              description: Request limit for the active window.
            X-RateLimit-Remaining:
              schema:
                type: integer
              description: Requests remaining after this request.
            X-RateLimit-Reset:
              schema:
                type: integer
              description: Unix time when the active window resets.
            Retry-After:
              schema:
                type: integer
              description: Seconds until retry when the request is rate limited.
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
        '422':
          description: Invalid request
          headers:
            X-RateLimit-Limit:
              schema:
                type: integer
              description: Request limit for the active window.
            X-RateLimit-Remaining:
              schema:
                type: integer
              description: Requests remaining after this request.
            X-RateLimit-Reset:
              schema:
                type: integer
              description: Unix time when the active window resets.
            Retry-After:
              schema:
                type: integer
              description: Seconds until retry when the request is rate limited.
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
  "/api/v1/sites/{slug}/content/{entry_uid}/brief":
    patch:
      operationId: updateApprovedContentBrief
      tags:
      - Sites
      summary: Correct an approved content brief
      description: Corrects an approved brief in place, including when drafts and
        an approved distribution package already exist. Supply at least one of `outline`,
        `target_keyword`, or a brand-contract field (`evidence`, `claim_safety_notes`,
        `cta`) — or supply only `editorial_contract`/`series_key`, which records the
        declaration without touching content or requiring feedback. Brand-contract
        fields merge into `seo_requirements` and are rejected on seo-lane briefs;
        `target_keyword` is not available for brand briefs. A keyword correction must
        match the calendar entry keyword already set through the entry PATCH. Unknown
        fields are rejected. Preserves brief and entry UIDs and does not change entry,
        draft, package, review, execution, or scheduling state. The correction is
        journalled with API-key attribution and required operator feedback. Requires
        a `write`-scoped API key.
      security:
      - bearerAuth: []
      parameters:
      - name: slug
        in: path
        required: true
        schema:
          type: string
        description: Site slug.
      - name: entry_uid
        in: path
        required: true
        schema:
          type: string
        description: Content entry UID returned by the content endpoint.
      - name: outline
        in: query
        required: false
        schema:
          type: string
        description: The complete corrected outline.
      - name: target_keyword
        in: query
        required: false
        schema:
          type: string
        description: The corrected brief keyword; must equal the entry target keyword.
      - name: evidence
        in: query
        required: false
        schema:
          type: array
          items:
            type: object
            required:
            - source
            - claim
            additionalProperties: false
            properties:
              source:
                type: string
              claim:
                type: string
        description: Brand-lane sourced evidence merged into seo_requirements. Rejected
          on seo-lane briefs.
      - name: claim_safety_notes
        in: query
        required: false
        schema:
          type: string
        description: Brand-lane claim-safety boundaries merged into seo_requirements.
          Rejected on seo-lane briefs.
      - name: cta
        in: query
        required: false
        schema:
          type: string
        description: Brand-lane call to action merged into seo_requirements. Rejected
          on seo-lane briefs.
      - name: editorial_contract
        in: query
        required: false
        schema:
          type: string
          enum:
          - no_measurement_methodology
        description: Declare the piece's rubric content type. Send blank to clear.
      - name: series_key
        in: query
        required: false
        schema:
          type: string
        description: Declares this brief a part of a named series. Siblings sharing
          the key are excluded from the originality doctrine's corpus. Send blank
          to clear.
      - name: feedback
        in: query
        required: false
        schema:
          type: string
        description: Required reason for a governed content correction; not required
          when only declarations are supplied.
      responses:
        '200':
          description: ok
          headers:
            X-RateLimit-Limit:
              schema:
                type: integer
              description: Request limit for the active window.
            X-RateLimit-Remaining:
              schema:
                type: integer
              description: Requests remaining after this request.
            X-RateLimit-Reset:
              schema:
                type: integer
              description: Unix time when the active window resets.
            Retry-After:
              schema:
                type: integer
              description: Seconds until retry when the request is rate limited.
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/WorkflowMutationResponse"
        '401':
          description: Unauthorized
          headers:
            X-RateLimit-Limit:
              schema:
                type: integer
              description: Request limit for the active window.
            X-RateLimit-Remaining:
              schema:
                type: integer
              description: Requests remaining after this request.
            X-RateLimit-Reset:
              schema:
                type: integer
              description: Unix time when the active window resets.
            Retry-After:
              schema:
                type: integer
              description: Seconds until retry when the request is rate limited.
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
        '403':
          description: Forbidden
          headers:
            X-RateLimit-Limit:
              schema:
                type: integer
              description: Request limit for the active window.
            X-RateLimit-Remaining:
              schema:
                type: integer
              description: Requests remaining after this request.
            X-RateLimit-Reset:
              schema:
                type: integer
              description: Unix time when the active window resets.
            Retry-After:
              schema:
                type: integer
              description: Seconds until retry when the request is rate limited.
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
        '404':
          description: Not found
          headers:
            X-RateLimit-Limit:
              schema:
                type: integer
              description: Request limit for the active window.
            X-RateLimit-Remaining:
              schema:
                type: integer
              description: Requests remaining after this request.
            X-RateLimit-Reset:
              schema:
                type: integer
              description: Unix time when the active window resets.
            Retry-After:
              schema:
                type: integer
              description: Seconds until retry when the request is rate limited.
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
        '409':
          description: Invalid state
          headers:
            X-RateLimit-Limit:
              schema:
                type: integer
              description: Request limit for the active window.
            X-RateLimit-Remaining:
              schema:
                type: integer
              description: Requests remaining after this request.
            X-RateLimit-Reset:
              schema:
                type: integer
              description: Unix time when the active window resets.
            Retry-After:
              schema:
                type: integer
              description: Seconds until retry when the request is rate limited.
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
        '422':
          description: Invalid request
          headers:
            X-RateLimit-Limit:
              schema:
                type: integer
              description: Request limit for the active window.
            X-RateLimit-Remaining:
              schema:
                type: integer
              description: Requests remaining after this request.
            X-RateLimit-Reset:
              schema:
                type: integer
              description: Unix time when the active window resets.
            Retry-After:
              schema:
                type: integer
              description: Seconds until retry when the request is rate limited.
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
  "/api/v1/sites/{slug}/content/{entry_uid}/triage":
    patch:
      operationId: triageContentEntry
      tags:
      - Sites
      summary: Update content entry triage
      description: 'Writes a triage state and reason for a content entry so stale
        backlog can be cleaned up through the Bearer API. `archive` and `delete_candidate`
        set the resource status to `archived`, remove it from default active inventory,
        and record `archived_at`/`archived_by`/`archived_reason`/`pre_archive_status`.
        Triage is one-way: no triage_state — `keep_publish` included — ever clears
        `status=archived`. `POST .../restore` is the only unarchive path, and an archived
        entry''s next_step points at it. Archived rows remain readable through `GET
        /content?status=archived` or `status=all`. Requires a `write`-scoped API key.'
      security:
      - bearerAuth: []
      parameters:
      - name: slug
        in: path
        required: true
        schema:
          type: string
        description: Site slug.
      - name: entry_uid
        in: path
        required: true
        schema:
          type: string
        description: Content entry UID returned by the content endpoint.
      responses:
        '200':
          description: ok; archived resources return next_step action=restore_content,
            phase=archived, and the restore endpoint
          headers:
            X-RateLimit-Limit:
              schema:
                type: integer
              description: Request limit for the active window.
            X-RateLimit-Remaining:
              schema:
                type: integer
              description: Requests remaining after this request.
            X-RateLimit-Reset:
              schema:
                type: integer
              description: Unix time when the active window resets.
            Retry-After:
              schema:
                type: integer
              description: Seconds until retry when the request is rate limited.
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/WorkflowMutationResponse"
        '401':
          description: Unauthorized
          headers:
            X-RateLimit-Limit:
              schema:
                type: integer
              description: Request limit for the active window.
            X-RateLimit-Remaining:
              schema:
                type: integer
              description: Requests remaining after this request.
            X-RateLimit-Reset:
              schema:
                type: integer
              description: Unix time when the active window resets.
            Retry-After:
              schema:
                type: integer
              description: Seconds until retry when the request is rate limited.
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
        '403':
          description: Forbidden
          headers:
            X-RateLimit-Limit:
              schema:
                type: integer
              description: Request limit for the active window.
            X-RateLimit-Remaining:
              schema:
                type: integer
              description: Requests remaining after this request.
            X-RateLimit-Reset:
              schema:
                type: integer
              description: Unix time when the active window resets.
            Retry-After:
              schema:
                type: integer
              description: Seconds until retry when the request is rate limited.
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
        '404':
          description: Not found
          headers:
            X-RateLimit-Limit:
              schema:
                type: integer
              description: Request limit for the active window.
            X-RateLimit-Remaining:
              schema:
                type: integer
              description: Requests remaining after this request.
            X-RateLimit-Reset:
              schema:
                type: integer
              description: Unix time when the active window resets.
            Retry-After:
              schema:
                type: integer
              description: Seconds until retry when the request is rate limited.
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
        '422':
          description: Invalid params
          headers:
            X-RateLimit-Limit:
              schema:
                type: integer
              description: Request limit for the active window.
            X-RateLimit-Remaining:
              schema:
                type: integer
              description: Requests remaining after this request.
            X-RateLimit-Reset:
              schema:
                type: integer
              description: Unix time when the active window resets.
            Retry-After:
              schema:
                type: integer
              description: Seconds until retry when the request is rate limited.
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              "$ref": "#/components/schemas/TriageContentRequest"
  "/api/v1/sites/{slug}/content/{entry_uid}/restore":
    post:
      operationId: restoreContentEntry
      tags:
      - Sites
      summary: Restore an archived content entry
      description: Returns an out-of-pipeline content entry to the pipeline — both
        rows with `status=archived` and legacy rows hidden only by an `archive`/`delete_candidate`
        triage state. The restored status is the row's `pre_archive_status`; for rows
        without one it is derived from the artifacts that survived the archive (package
        or draft ⇒ `in_progress`, brief ⇒ `brief_generated`, otherwise `planned`),
        and a transient `brief_generating` prior status also falls back to that derivation.
        Brief, draft, package, review-queue gates, owner notes and cover images are
        untouched — restore only reverses the archival. Archive provenance is moved
        to `restored_from_archive` in the entry metadata, and an `archive`/`delete_candidate`
        triage state is cleared so the row re-enters active inventory. An in-pipeline
        entry returns 409 `invalid_state`. Another active entry already claiming the
        same keyword opportunity or normalized `target_keyword` returns 409 `keyword_claimed`
        with `colliding_entry_uid`. Requires a `write`-scoped API key.
      security:
      - bearerAuth: []
      parameters:
      - name: slug
        in: path
        required: true
        schema:
          type: string
        description: Site slug.
      - name: entry_uid
        in: path
        required: true
        schema:
          type: string
        description: Content entry UID returned by the content endpoint.
      responses:
        '200':
          description: ok
          headers:
            X-RateLimit-Limit:
              schema:
                type: integer
              description: Request limit for the active window.
            X-RateLimit-Remaining:
              schema:
                type: integer
              description: Requests remaining after this request.
            X-RateLimit-Reset:
              schema:
                type: integer
              description: Unix time when the active window resets.
            Retry-After:
              schema:
                type: integer
              description: Seconds until retry when the request is rate limited.
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/WorkflowMutationResponse"
        '401':
          description: Unauthorized
          headers:
            X-RateLimit-Limit:
              schema:
                type: integer
              description: Request limit for the active window.
            X-RateLimit-Remaining:
              schema:
                type: integer
              description: Requests remaining after this request.
            X-RateLimit-Reset:
              schema:
                type: integer
              description: Unix time when the active window resets.
            Retry-After:
              schema:
                type: integer
              description: Seconds until retry when the request is rate limited.
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
        '403':
          description: Forbidden
          headers:
            X-RateLimit-Limit:
              schema:
                type: integer
              description: Request limit for the active window.
            X-RateLimit-Remaining:
              schema:
                type: integer
              description: Requests remaining after this request.
            X-RateLimit-Reset:
              schema:
                type: integer
              description: Unix time when the active window resets.
            Retry-After:
              schema:
                type: integer
              description: Seconds until retry when the request is rate limited.
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
        '404':
          description: Not found
          headers:
            X-RateLimit-Limit:
              schema:
                type: integer
              description: Request limit for the active window.
            X-RateLimit-Remaining:
              schema:
                type: integer
              description: Requests remaining after this request.
            X-RateLimit-Reset:
              schema:
                type: integer
              description: Unix time when the active window resets.
            Retry-After:
              schema:
                type: integer
              description: Seconds until retry when the request is rate limited.
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
        '409':
          description: Entry is not archived, or another active entry already claims
            this keyword
          headers:
            X-RateLimit-Limit:
              schema:
                type: integer
              description: Request limit for the active window.
            X-RateLimit-Remaining:
              schema:
                type: integer
              description: Requests remaining after this request.
            X-RateLimit-Reset:
              schema:
                type: integer
              description: Unix time when the active window resets.
            Retry-After:
              schema:
                type: integer
              description: Seconds until retry when the request is rate limited.
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
  "/api/v1/sites/{slug}/content/{entry_uid}/reschedule":
    patch:
      operationId: rescheduleContentEntry
      tags:
      - Sites
      summary: Reschedule content entry
      description: Moves an unpublished planned, brief-generating, approved-draft,
        or packaged content entry to an explicit calendar date without changing draft,
        package, review, claim, campaign, or publication state. A packaged entry remains
        reschedulable while a replacement draft is under review. Requires a `write`-scoped
        API key.
      security:
      - bearerAuth: []
      parameters:
      - name: slug
        in: path
        required: true
        schema:
          type: string
        description: Site slug.
      - name: entry_uid
        in: path
        required: true
        schema:
          type: string
        description: Content entry UID returned by the content endpoint.
      responses:
        '200':
          description: ok
          headers:
            X-RateLimit-Limit:
              schema:
                type: integer
              description: Request limit for the active window.
            X-RateLimit-Remaining:
              schema:
                type: integer
              description: Requests remaining after this request.
            X-RateLimit-Reset:
              schema:
                type: integer
              description: Unix time when the active window resets.
            Retry-After:
              schema:
                type: integer
              description: Seconds until retry when the request is rate limited.
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/WorkflowMutationResponse"
        '401':
          description: Unauthorized
          headers:
            X-RateLimit-Limit:
              schema:
                type: integer
              description: Request limit for the active window.
            X-RateLimit-Remaining:
              schema:
                type: integer
              description: Requests remaining after this request.
            X-RateLimit-Reset:
              schema:
                type: integer
              description: Unix time when the active window resets.
            Retry-After:
              schema:
                type: integer
              description: Seconds until retry when the request is rate limited.
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
        '403':
          description: Forbidden
          headers:
            X-RateLimit-Limit:
              schema:
                type: integer
              description: Request limit for the active window.
            X-RateLimit-Remaining:
              schema:
                type: integer
              description: Requests remaining after this request.
            X-RateLimit-Reset:
              schema:
                type: integer
              description: Unix time when the active window resets.
            Retry-After:
              schema:
                type: integer
              description: Seconds until retry when the request is rate limited.
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
        '404':
          description: Not found
          headers:
            X-RateLimit-Limit:
              schema:
                type: integer
              description: Request limit for the active window.
            X-RateLimit-Remaining:
              schema:
                type: integer
              description: Requests remaining after this request.
            X-RateLimit-Reset:
              schema:
                type: integer
              description: Unix time when the active window resets.
            Retry-After:
              schema:
                type: integer
              description: Seconds until retry when the request is rate limited.
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
        '409':
          description: Invalid state
          headers:
            X-RateLimit-Limit:
              schema:
                type: integer
              description: Request limit for the active window.
            X-RateLimit-Remaining:
              schema:
                type: integer
              description: Requests remaining after this request.
            X-RateLimit-Reset:
              schema:
                type: integer
              description: Unix time when the active window resets.
            Retry-After:
              schema:
                type: integer
              description: Seconds until retry when the request is rate limited.
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
        '422':
          description: Invalid params
          headers:
            X-RateLimit-Limit:
              schema:
                type: integer
              description: Request limit for the active window.
            X-RateLimit-Remaining:
              schema:
                type: integer
              description: Requests remaining after this request.
            X-RateLimit-Reset:
              schema:
                type: integer
              description: Unix time when the active window resets.
            Retry-After:
              schema:
                type: integer
              description: Seconds until retry when the request is rate limited.
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
              - scheduled_date
              properties:
                scheduled_date:
                  type: string
                  format: date
                  description: The new scheduled date in YYYY-MM-DD format.
  "/api/v1/sites/{slug}/content/{entry_uid}/certify_draft":
    post:
      operationId: certifyContentDraft
      tags:
      - Sites
      summary: Certify content draft for human review
      description: Editorially certifies a finished draft into `ready_for_review`
        when automated blockers and critical failures are empty but the AI rubric
        verdict remains revise. Does not bypass claim-risk or distribution gates.
        Requires a non-blank `note` and a `write`-scoped API key.
      security:
      - bearerAuth: []
      parameters:
      - name: slug
        in: path
        required: true
        schema:
          type: string
        description: Site slug.
      - name: entry_uid
        in: path
        required: true
        schema:
          type: string
        description: Content entry UID returned by the content endpoint.
      - name: note
        in: query
        required: true
        schema:
          type: string
        description: Editorial certification rationale (also accepted as certification_note
          in the JSON body).
      responses:
        '200':
          description: ok
          headers:
            X-RateLimit-Limit:
              schema:
                type: integer
              description: Request limit for the active window.
            X-RateLimit-Remaining:
              schema:
                type: integer
              description: Requests remaining after this request.
            X-RateLimit-Reset:
              schema:
                type: integer
              description: Unix time when the active window resets.
            Retry-After:
              schema:
                type: integer
              description: Seconds until retry when the request is rate limited.
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/WorkflowMutationResponse"
        '401':
          description: Unauthorized
          headers:
            X-RateLimit-Limit:
              schema:
                type: integer
              description: Request limit for the active window.
            X-RateLimit-Remaining:
              schema:
                type: integer
              description: Requests remaining after this request.
            X-RateLimit-Reset:
              schema:
                type: integer
              description: Unix time when the active window resets.
            Retry-After:
              schema:
                type: integer
              description: Seconds until retry when the request is rate limited.
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
        '403':
          description: Forbidden
          headers:
            X-RateLimit-Limit:
              schema:
                type: integer
              description: Request limit for the active window.
            X-RateLimit-Remaining:
              schema:
                type: integer
              description: Requests remaining after this request.
            X-RateLimit-Reset:
              schema:
                type: integer
              description: Unix time when the active window resets.
            Retry-After:
              schema:
                type: integer
              description: Seconds until retry when the request is rate limited.
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
        '404':
          description: Not found
          headers:
            X-RateLimit-Limit:
              schema:
                type: integer
              description: Request limit for the active window.
            X-RateLimit-Remaining:
              schema:
                type: integer
              description: Requests remaining after this request.
            X-RateLimit-Reset:
              schema:
                type: integer
              description: Unix time when the active window resets.
            Retry-After:
              schema:
                type: integer
              description: Seconds until retry when the request is rate limited.
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
        '409':
          description: Conflict — `stale_review_report` (next_step submit_draft if
            review-run budget remains, else revision_recovery), `evidence_gate_failed`
            (request_changes if writer budget remains, else revision_recovery), `blockers_present`
            (request_changes / revision_recovery), `critical_failures` (request_changes
            / revision_recovery), `review_required` (submit_draft), or `invalid_state`
            (next_step wait for in-flight ReviewAgent)
          headers:
            X-RateLimit-Limit:
              schema:
                type: integer
              description: Request limit for the active window.
            X-RateLimit-Remaining:
              schema:
                type: integer
              description: Requests remaining after this request.
            X-RateLimit-Reset:
              schema:
                type: integer
              description: Unix time when the active window resets.
            Retry-After:
              schema:
                type: integer
              description: Seconds until retry when the request is rate limited.
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/DraftConflictErrorResponse"
        '422':
          description: Invalid params
          headers:
            X-RateLimit-Limit:
              schema:
                type: integer
              description: Request limit for the active window.
            X-RateLimit-Remaining:
              schema:
                type: integer
              description: Requests remaining after this request.
            X-RateLimit-Reset:
              schema:
                type: integer
              description: Unix time when the active window resets.
            Retry-After:
              schema:
                type: integer
              description: Seconds until retry when the request is rate limited.
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
  "/api/v1/sites/{slug}/jobs/{job_id}":
    get:
      operationId: getAgentRunStatus
      tags:
      - Sites
      summary: Get agent run status
      description: Returns the current status for a queued or completed agent run.
        For `SiteAuditorAgent` jobs, `status`, `started_at` and `completed_at` report
        the audit's own execution once the run reaches a terminal status, while `run_uid`
        identifies the accounting run; `audit_cycle` names this run's audit cycle
        and is null for every other agent.
      security:
      - bearerAuth: []
      parameters:
      - name: slug
        in: path
        required: true
        schema:
          type: string
        description: Site slug.
      - name: job_id
        in: path
        required: true
        schema:
          type: string
        description: Job ID returned by an async workflow response.
      responses:
        '200':
          description: ok
          headers:
            X-RateLimit-Limit:
              schema:
                type: integer
              description: Request limit for the active window.
            X-RateLimit-Remaining:
              schema:
                type: integer
              description: Requests remaining after this request.
            X-RateLimit-Reset:
              schema:
                type: integer
              description: Unix time when the active window resets.
            Retry-After:
              schema:
                type: integer
              description: Seconds until retry when the request is rate limited.
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/AgentRunStatusResponse"
        '401':
          description: Unauthorized
          headers:
            X-RateLimit-Limit:
              schema:
                type: integer
              description: Request limit for the active window.
            X-RateLimit-Remaining:
              schema:
                type: integer
              description: Requests remaining after this request.
            X-RateLimit-Reset:
              schema:
                type: integer
              description: Unix time when the active window resets.
            Retry-After:
              schema:
                type: integer
              description: Seconds until retry when the request is rate limited.
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
        '403':
          description: Forbidden
          headers:
            X-RateLimit-Limit:
              schema:
                type: integer
              description: Request limit for the active window.
            X-RateLimit-Remaining:
              schema:
                type: integer
              description: Requests remaining after this request.
            X-RateLimit-Reset:
              schema:
                type: integer
              description: Unix time when the active window resets.
            Retry-After:
              schema:
                type: integer
              description: Seconds until retry when the request is rate limited.
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
        '404':
          description: Not found
          headers:
            X-RateLimit-Limit:
              schema:
                type: integer
              description: Request limit for the active window.
            X-RateLimit-Remaining:
              schema:
                type: integer
              description: Requests remaining after this request.
            X-RateLimit-Reset:
              schema:
                type: integer
              description: Unix time when the active window resets.
            Retry-After:
              schema:
                type: integer
              description: Seconds until retry when the request is rate limited.
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
  "/api/v1/sites/{slug}/agent_runs/{job_id}":
    get:
      operationId: getLegacyAgentRunStatus
      tags:
      - Sites
      summary: Get agent run status (legacy alias)
      description: Legacy alias for `/api/v1/sites/{slug}/jobs/{job_id}`. For `SiteAuditorAgent`
        jobs, `status`, `started_at` and `completed_at` report the audit's own execution
        once the run reaches a terminal status, while `run_uid` identifies the accounting
        run; `audit_cycle` names this run's audit cycle and is null for every other
        agent.
      security:
      - bearerAuth: []
      parameters:
      - name: slug
        in: path
        required: true
        schema:
          type: string
        description: Site slug.
      - name: job_id
        in: path
        required: true
        schema:
          type: string
        description: Job ID returned by an async workflow response.
      responses:
        '200':
          description: ok
          headers:
            X-RateLimit-Limit:
              schema:
                type: integer
              description: Request limit for the active window.
            X-RateLimit-Remaining:
              schema:
                type: integer
              description: Requests remaining after this request.
            X-RateLimit-Reset:
              schema:
                type: integer
              description: Unix time when the active window resets.
            Retry-After:
              schema:
                type: integer
              description: Seconds until retry when the request is rate limited.
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/AgentRunStatusResponse"
        '401':
          description: Unauthorized
          headers:
            X-RateLimit-Limit:
              schema:
                type: integer
              description: Request limit for the active window.
            X-RateLimit-Remaining:
              schema:
                type: integer
              description: Requests remaining after this request.
            X-RateLimit-Reset:
              schema:
                type: integer
              description: Unix time when the active window resets.
            Retry-After:
              schema:
                type: integer
              description: Seconds until retry when the request is rate limited.
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
        '403':
          description: Forbidden
          headers:
            X-RateLimit-Limit:
              schema:
                type: integer
              description: Request limit for the active window.
            X-RateLimit-Remaining:
              schema:
                type: integer
              description: Requests remaining after this request.
            X-RateLimit-Reset:
              schema:
                type: integer
              description: Unix time when the active window resets.
            Retry-After:
              schema:
                type: integer
              description: Seconds until retry when the request is rate limited.
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
        '404':
          description: Not found
          headers:
            X-RateLimit-Limit:
              schema:
                type: integer
              description: Request limit for the active window.
            X-RateLimit-Remaining:
              schema:
                type: integer
              description: Requests remaining after this request.
            X-RateLimit-Reset:
              schema:
                type: integer
              description: Unix time when the active window resets.
            Retry-After:
              schema:
                type: integer
              description: Seconds until retry when the request is rate limited.
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
  "/api/v1/mcp/manifest":
    get:
      operationId: getMcpManifest
      tags:
      - MCP
      summary: Get MCP manifest
      description: Returns the TrustGrowth MCP discovery manifest.
      security:
      - bearerAuth: []
      parameters: []
      responses:
        '200':
          description: ok
          headers:
            X-RateLimit-Limit:
              schema:
                type: integer
              description: Request limit for the active window.
            X-RateLimit-Remaining:
              schema:
                type: integer
              description: Requests remaining after this request.
            X-RateLimit-Reset:
              schema:
                type: integer
              description: Unix time when the active window resets.
            Retry-After:
              schema:
                type: integer
              description: Seconds until retry when the request is rate limited.
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/McpManifest"
        '401':
          description: Unauthorized
          headers:
            X-RateLimit-Limit:
              schema:
                type: integer
              description: Request limit for the active window.
            X-RateLimit-Remaining:
              schema:
                type: integer
              description: Requests remaining after this request.
            X-RateLimit-Reset:
              schema:
                type: integer
              description: Unix time when the active window resets.
            Retry-After:
              schema:
                type: integer
              description: Seconds until retry when the request is rate limited.
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
        '403':
          description: Forbidden
          headers:
            X-RateLimit-Limit:
              schema:
                type: integer
              description: Request limit for the active window.
            X-RateLimit-Remaining:
              schema:
                type: integer
              description: Requests remaining after this request.
            X-RateLimit-Reset:
              schema:
                type: integer
              description: Unix time when the active window resets.
            Retry-After:
              schema:
                type: integer
              description: Seconds until retry when the request is rate limited.
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
  "/api/v1/capabilities/v1":
    get:
      operationId: getCapabilityManifestV1
      tags:
      - MCP
      summary: Get versioned capability manifest
      description: Generated reconciliation of public OpenAPI operations, public MCP
        tools, and the finite dark-surface allowlist.
      security:
      - bearerAuth: []
      parameters: []
      responses:
        '200':
          description: ok
          headers:
            X-RateLimit-Limit:
              schema:
                type: integer
              description: Request limit for the active window.
            X-RateLimit-Remaining:
              schema:
                type: integer
              description: Requests remaining after this request.
            X-RateLimit-Reset:
              schema:
                type: integer
              description: Unix time when the active window resets.
            Retry-After:
              schema:
                type: integer
              description: Seconds until retry when the request is rate limited.
          content:
            application/json:
              schema:
                type: object
        '401':
          description: Unauthorized
          headers:
            X-RateLimit-Limit:
              schema:
                type: integer
              description: Request limit for the active window.
            X-RateLimit-Remaining:
              schema:
                type: integer
              description: Requests remaining after this request.
            X-RateLimit-Reset:
              schema:
                type: integer
              description: Unix time when the active window resets.
            Retry-After:
              schema:
                type: integer
              description: Seconds until retry when the request is rate limited.
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
        '403':
          description: Forbidden
          headers:
            X-RateLimit-Limit:
              schema:
                type: integer
              description: Request limit for the active window.
            X-RateLimit-Remaining:
              schema:
                type: integer
              description: Requests remaining after this request.
            X-RateLimit-Reset:
              schema:
                type: integer
              description: Unix time when the active window resets.
            Retry-After:
              schema:
                type: integer
              description: Seconds until retry when the request is rate limited.
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
components:
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      bearerFormat: API Key
      description: 'Use a TrustGrowth API key in the Authorization header: `Bearer
        tg_live_...`.'
  schemas:
    ErrorResponse:
      type: object
      required:
      - error
      properties:
        error:
          type: object
          required:
          - code
          - message
          properties:
            code:
              type: string
            message:
              type: string
            retry_after:
              type: integer
              nullable: true
          additionalProperties: true
    InsufficientCreditsErrorResponse:
      type: object
      required:
      - error
      properties:
        error:
          type: object
          required:
          - code
          - message
          properties:
            code:
              type: string
              enum:
              - insufficient_credits
            message:
              type: string
            retry_after:
              type: integer
              nullable: true
          additionalProperties: true
    AlreadyRunningErrorResponse:
      type: object
      required:
      - error
      properties:
        error:
          type: object
          required:
          - code
          - message
          properties:
            code:
              type: string
              enum:
              - already_running
            message:
              type: string
            retry_after:
              type: integer
              nullable: true
          additionalProperties: true
    RateLimitedErrorResponse:
      type: object
      required:
      - error
      properties:
        error:
          type: object
          required:
          - code
          - message
          properties:
            code:
              type: string
              enum:
              - rate_limited
            message:
              type: string
            retry_after:
              type: integer
              nullable: true
          additionalProperties: true
    DraftConflictErrorResponse:
      type: object
      description: HTTP 409 envelope for draft evidence-gate, review-run cap, and
        certify refusals. `error.next_step` is the workflow envelope already used
        by draft endpoints.
      required:
      - error
      properties:
        error:
          type: object
          required:
          - code
          - message
          properties:
            code:
              type: string
              enum:
              - stale_review_report
              - evidence_gate_failed
              - evidence_already_reviewed
              - review_limit_exhausted
              - invalid_state
              - blockers_present
              - critical_failures
              - review_required
              description: "`stale_review_report` → submit_draft (or revision_recovery
                / certify_draft as eligible); `evidence_gate_failed` → request_changes
                if writer budget remains, else revision_recovery; `evidence_already_reviewed`
                → certify_draft if live revise, else approve_draft; `review_limit_exhausted`
                → revision_recovery; `invalid_state` → wait (in-flight ReviewAgent);
                `blockers_present` / `critical_failures` → request_changes / revision_recovery;
                `review_required` → submit_draft."
            message:
              type: string
            next_step:
              "$ref": "#/components/schemas/WorkflowNextStep"
            retry_after:
              type: integer
              nullable: true
          additionalProperties: true
    PlanLimitErrorResponse:
      type: object
      required:
      - error
      properties:
        error:
          type: object
          required:
          - code
          - message
          - required_feature
          properties:
            code:
              type: string
              enum:
              - plan_limit
            message:
              type: string
            required_feature:
              type: string
            upgrade_to:
              type: string
              nullable: true
              enum:
              - starter
              - pro
              - growth
              description: Public plan slug recommended for upgrade. This is a customer-facing
                identifier; internal billing keys may differ.
    ComingSoonErrorResponse:
      type: object
      required:
      - error
      properties:
        error:
          type: object
          required:
          - code
          - message
          - required_feature
          - coming_soon
          - waitlist_url
          properties:
            code:
              type: string
              enum:
              - coming_soon
            message:
              type: string
            required_feature:
              type: string
            coming_soon:
              type: boolean
              enum:
              - true
            waitlist_url:
              type: string
              format: uri
    VisibilityGate:
      oneOf:
      - "$ref": "#/components/schemas/PlanLimitErrorResponse"
      - "$ref": "#/components/schemas/ComingSoonErrorResponse"
    VisibilityStageBase:
      type: object
      required:
      - nature
      - captured_on
      - stale
      - status
      properties:
        nature:
          type: string
          enum:
          - deterministic
          - census
          - sample
          - observed
        captured_on:
          type: string
          format: date
          nullable: true
        stale:
          type: boolean
        status:
          type: string
          enum:
          - measured
          - not_measured
          - withheld
      additionalProperties: true
    VisibilityReachStage:
      allOf:
      - "$ref": "#/components/schemas/VisibilityStageBase"
      - type: object
        required:
        - reachable_count
        - llms_txt
        - bots
        properties:
          reachable_count:
            type: integer
            nullable: true
          llms_txt:
            type: string
            enum:
            - present
            - absent
            - unknown
            nullable: true
          bots:
            type: array
            items:
              type: object
              required:
              - bot
              - state
              properties:
                bot:
                  type: string
                  nullable: true
                state:
                  type: string
                  enum:
                  - present
                  - absent
                  - unknown
              nullable: true
    VisibilityReadableStage:
      allOf:
      - "$ref": "#/components/schemas/VisibilityStageBase"
      - type: object
        required:
        - score
        - groups
        - missing_reason
        properties:
          score:
            type: number
            nullable: true
          groups:
            type: object
            nullable: true
            additionalProperties:
              type: number
              nullable: true
          missing_reason:
            type: string
            nullable: true
    VisibilityRetrievedStage:
      allOf:
      - "$ref": "#/components/schemas/VisibilityStageBase"
      - type: object
        required:
        - cited_keyword_count
        - citations
        properties:
          cited_keyword_count:
            type: integer
            nullable: true
          citations:
            type: array
            items:
              type: object
              properties:
                keyword:
                  type: string
                rank_group:
                  type: integer
                  nullable: true
              additionalProperties: false
            nullable: true
    VisibilityRecalledStage:
      allOf:
      - "$ref": "#/components/schemas/VisibilityStageBase"
      - type: object
        required:
        - rate
        - interval
        - "n"
        - engines
        - answered_count
        - non_answer_count
        - by_engine
        - withheld_reason
        - engine_set_changed_at
        properties:
          rate:
            type: number
            nullable: true
          interval:
            type: object
            nullable: true
            required:
            - low
            - high
            - method
            properties:
              low:
                type: number
              high:
                type: number
              method:
                type: string
                enum:
                - wilson
          "n":
            type: number
            minimum: 0
          engines:
            type: array
            items:
              type: string
              enum:
              - anthropic
              - openai
              - gemini
            uniqueItems: true
          answered_count:
            type: integer
          non_answer_count:
            type: integer
          by_engine:
            type: object
            additionalProperties:
              type: object
              required:
              - answered
              - mentions
              - non_mentions
              - non_answers
              properties:
                answered:
                  type: integer
                mentions:
                  type: integer
                non_mentions:
                  type: integer
                non_answers:
                  type: integer
              additionalProperties: false
          withheld_reason:
            type: string
            enum:
            - interval_wider_than_rate
            nullable: true
          engine_set_changed_at:
            type: string
            format: date
            nullable: true
    VisibilityImpactStage:
      allOf:
      - "$ref": "#/components/schemas/VisibilityStageBase"
      - type: object
        required:
        - lift
        - current_impressions
        - prior_impressions
        - current_clicks
        - prior_clicks
        - branded_query_count
        - window_days
        - missing_reason
        - disclaimer
        properties:
          lift:
            type: number
            nullable: true
          current_impressions:
            type: integer
          prior_impressions:
            type: integer
          current_clicks:
            type: integer
          prior_clicks:
            type: integer
          branded_query_count:
            type: integer
          window_days:
            type: integer
          missing_reason:
            type: string
            nullable: true
          disclaimer:
            type: string
            enum:
            - Branded-search movement is correlated with AI visibility; it does not
              establish attribution or causation.
    SiteVisibility:
      type: object
      additionalProperties: false
      required:
      - reach
      - readable
      - retrieved
      - recalled
      - impact
      properties:
        reach:
          oneOf:
          - "$ref": "#/components/schemas/VisibilityReachStage"
          - "$ref": "#/components/schemas/VisibilityGate"
        readable:
          oneOf:
          - "$ref": "#/components/schemas/VisibilityReadableStage"
          - "$ref": "#/components/schemas/VisibilityGate"
        retrieved:
          oneOf:
          - "$ref": "#/components/schemas/VisibilityRetrievedStage"
          - "$ref": "#/components/schemas/VisibilityGate"
        recalled:
          oneOf:
          - "$ref": "#/components/schemas/VisibilityRecalledStage"
          - "$ref": "#/components/schemas/VisibilityGate"
        impact:
          oneOf:
          - "$ref": "#/components/schemas/VisibilityImpactStage"
          - "$ref": "#/components/schemas/VisibilityGate"
    SiteVisibilityResponse:
      type: object
      required:
      - data
      - meta
      properties:
        data:
          "$ref": "#/components/schemas/SiteVisibility"
        meta:
          "$ref": "#/components/schemas/Meta"
    DailySnapshot:
      type: object
      additionalProperties: true
    Strategy:
      type: object
      required:
      - type
      - uid
      - status
      - narrative
      - publishing_frequency
      - growth_cycle_uid
      - approved_at
      - created_at
      - updated_at
      properties:
        type:
          type: string
          enum:
          - strategy
        uid:
          type: string
        status:
          type: string
        narrative:
          type: string
          nullable: true
        publishing_frequency:
          type: object
          nullable: true
          additionalProperties: true
        growth_cycle_uid:
          type: string
          nullable: true
        approved_at:
          type: string
          format: date-time
          nullable: true
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time
    StrategyResponse:
      type: object
      required:
      - data
      - meta
      properties:
        data:
          "$ref": "#/components/schemas/Strategy"
        meta:
          "$ref": "#/components/schemas/Meta"
    ContentEntryCreateRequest:
      type: object
      required:
      - title
      - content_type
      - angle
      - lane
      properties:
        title:
          type: string
        content_type:
          type: string
          enum:
          - article
          - blog_post
          - comparison
          - listicle
          - pillar_page
          - faq_page
          - glossary_definition
          - data_study
          - tutorial
          - case_study
          - product_update
          - changelog
          - linkedin_post
          - twitter_thread
          - twitter_post
        angle:
          type: string
        lane:
          type: string
          enum:
          - seo
          - brand
          description: Required content lane. `seo` is judged against a keyword; `brand`
            is not.
        scheduled_date:
          type: string
          format: date
          nullable: true
        priority:
          type: integer
          nullable: true
    RecordPublicationRequest:
      type: object
      description: External publication attributes. Omit every field (empty body)
        to bridge-publish article content to the internal blog instead; social records
        must set channel and external_url.
      properties:
        channel:
          type: string
          description: Distribution channel, such as x, twitter_post, linkedin_post,
            linkedin, social, or blog.
        status:
          type: string
          description: Publication status; use published once the content is live.
        external_url:
          type: string
          description: Live URL of the publication. Required for social channels;
            the host must match the channel (for example x.com for channel x).
        external_post_id:
          type: string
          nullable: true
          description: Platform-native post identifier, when known.
        published_at:
          type: string
          format: date-time
          nullable: true
        record_back_attestation_uid:
          type: string
          minLength: 12
          maxLength: 12
          description: Opaque UID issued by the server-held claim_review authority.
        metadata:
          type: object
          additionalProperties: true
          nullable: true
    RecordBackAttestationRequest:
      type: object
      additionalProperties: false
      required:
      - channel
      - external_url
      - evidence_refs
      properties:
        channel:
          type: string
        external_url:
          type: string
          format: uri
        external_post_id:
          type: string
          nullable: true
        expected_payload_fingerprint:
          type: string
          pattern: "^[0-9a-f]{64}$"
        published_body:
          type: string
          minLength: 1
          maxLength: 10000
          nullable: true
          description: Exact immutable body already published on the bound live URL;
            its SHA-256 becomes the attested payload fingerprint.
        expires_at:
          type: string
          format: date-time
          nullable: true
        evidence_refs:
          type: array
          minItems: 1
          maxItems: 10
          items:
            type: string
            pattern: "^TEC-[0-9]+$"
        evidence_note:
          type: string
          maxLength: 1000
          nullable: true
    RecordBackAttestationResponse:
      type: object
      required:
      - data
      - meta
      properties:
        data:
          type: object
          required:
          - type
          - uid
          - entry_uid
          - package_uid
          - channel
          - external_url
          - payload_fingerprint
          - issuer_scope
          - issued_at
          - evidence
          properties:
            type:
              type: string
              enum:
              - record_back_attestation
            uid:
              type: string
            entry_uid:
              type: string
            package_uid:
              type: string
            channel:
              type: string
            external_url:
              type: string
              format: uri
            external_post_id:
              type: string
              nullable: true
            payload_fingerprint:
              type: string
            issuer_scope:
              type: string
              enum:
              - claim_review
            issued_at:
              type: string
              format: date-time
            expires_at:
              type: string
              format: date-time
              nullable: true
            evidence:
              type: object
              additionalProperties: true
        meta:
          "$ref": "#/components/schemas/Meta"
    ProofLinks:
      type: object
      required:
      - url
      - short_url
      - api_url
      properties:
        url:
          type: string
          format: uri
        short_url:
          type: string
          format: uri
        api_url:
          type: string
          format: uri
    PublicProofSite:
      type: object
      required:
      - slug
      - name
      - domain
      - language
      - story_mode
      properties:
        slug:
          type: string
        name:
          type: string
        domain:
          type: string
        language:
          type: string
        story_mode:
          type: boolean
        verified_since:
          type: string
          format: date-time
          nullable: true
        published_on:
          type: string
          format: date
          nullable: true
        updated_on:
          type: string
          format: date
          nullable: true
    PublicProofNarrative:
      type: object
      required:
      - title
      - description
      - author_name
      properties:
        title:
          type: string
        description:
          type: string
        author_name:
          type: string
    PublicProofSummary:
      type: object
      required:
      - overall_score
      - score_delta
      - click_growth_pct
      - keyword_count
      - keywords_in_top_10
      - published_content_count
      - geo_score
      - geo_eligible
      - total_clicks
      - total_impressions
      - latest_clicks
      - latest_impressions
      - latest_position
      properties:
        overall_score:
          type: integer
          nullable: true
        score_delta:
          type: integer
          nullable: true
        click_growth_pct:
          type: integer
        keyword_count:
          type: integer
        keywords_in_top_10:
          type: integer
        published_content_count:
          type: integer
        geo_score:
          type: integer
          nullable: true
        geo_eligible:
          type: boolean
        total_clicks:
          type: integer
        total_impressions:
          type: integer
        latest_clicks:
          type: integer
        latest_impressions:
          type: integer
        latest_position:
          type: number
          nullable: true
    PublicProofLatestScore:
      type: object
      nullable: true
      required:
      - score_date
      - overall_score
      - technical_score
      - content_score
      - performance_score
      - growth_score
      - eeat_score
      - authority_score
      - visibility_score
      - geo_score
      - eeat_experience
      - eeat_expertise
      - eeat_authoritativeness
      - eeat_trustworthiness
      - signal_evidence_score_date
      properties:
        score_date:
          type: string
          format: date
        overall_score:
          type: integer
          nullable: true
        technical_score:
          type: integer
          nullable: true
        content_score:
          type: integer
          nullable: true
        performance_score:
          type: integer
          nullable: true
        growth_score:
          type: integer
          nullable: true
        eeat_score:
          type: integer
          nullable: true
        authority_score:
          type: integer
          nullable: true
        visibility_score:
          type: integer
          nullable: true
        geo_score:
          type: integer
          nullable: true
        eeat_experience:
          type: integer
          nullable: true
        eeat_expertise:
          type: integer
          nullable: true
        eeat_authoritativeness:
          type: integer
          nullable: true
        eeat_trustworthiness:
          type: integer
          nullable: true
        signal_evidence_score_date:
          type: string
          format: date
          nullable: true
    PublicProofScores:
      type: object
      required:
      - latest
      - panels
      - eeat_breakdown
      - trajectory
      - evidence
      - pillar_states
      properties:
        latest:
          "$ref": "#/components/schemas/PublicProofLatestScore"
        panels:
          type: object
          properties:
            technical:
              type: integer
              nullable: true
            content:
              type: integer
              nullable: true
            performance:
              type: integer
              nullable: true
            growth:
              type: integer
              nullable: true
            eeat:
              type: integer
              nullable: true
            authority:
              type: integer
              nullable: true
            visibility:
              type: integer
              nullable: true
            geo:
              type: integer
              nullable: true
        eeat_breakdown:
          type: object
          properties:
            experience:
              type: integer
              nullable: true
            expertise:
              type: integer
              nullable: true
            authoritativeness:
              type: integer
              nullable: true
            trustworthiness:
              type: integer
              nullable: true
        trajectory:
          type: array
          items:
            type: object
            required:
            - score_date
            - overall_score
            - technical_score
            - content_score
            - performance_score
            - growth_score
            - eeat_score
            - authority_score
            - visibility_score
            - geo_score
            properties:
              score_date:
                type: string
                format: date
              overall_score:
                type: integer
                nullable: true
              technical_score:
                type: integer
                nullable: true
              content_score:
                type: integer
                nullable: true
              performance_score:
                type: integer
                nullable: true
              growth_score:
                type: integer
                nullable: true
              eeat_score:
                type: integer
                nullable: true
              authority_score:
                type: integer
                nullable: true
              visibility_score:
                type: integer
                nullable: true
              geo_score:
                type: integer
                nullable: true
        evidence:
          type: object
          required:
          - score_state
          - provisional
          - uncertainty
          - present
          - total
          - confidence_weighted_coverage
          properties:
            score_state:
              type: string
              nullable: true
            provisional:
              type: boolean
            uncertainty:
              type: object
              required:
              - lower
              - upper
              properties:
                lower:
                  type: number
                  nullable: true
                upper:
                  type: number
                  nullable: true
            present:
              type: integer
              nullable: true
            total:
              type: integer
              nullable: true
            confidence_weighted_coverage:
              type: number
              nullable: true
        pillar_states:
          type: object
          additionalProperties:
            type: object
            required:
            - state
            - confidence
            - missing_reason
            properties:
              state:
                type: string
                nullable: true
              confidence:
                type: number
                nullable: true
              missing_reason:
                type: string
                nullable: true
    PublicProofTrustGrowthScore:
      type: object
      required:
      - value
      - tier
      - rank_eligible
      - score_date
      properties:
        value:
          type: integer
          nullable: true
        tier:
          type: string
          nullable: true
        rank_eligible:
          type: boolean
        score_date:
          type: string
          format: date
          nullable: true
    PublicProofTraffic:
      type: object
      required:
      - latest_snapshot
      - weekly_snapshots
      properties:
        latest_snapshot:
          type: object
          nullable: true
          properties:
            snapshot_date:
              type: string
              format: date
            organic_clicks:
              type: integer
              nullable: true
            organic_impressions:
              type: integer
              nullable: true
            average_position:
              type: number
              nullable: true
            indexed_pages:
              type: integer
              nullable: true
        weekly_snapshots:
          type: array
          items:
            type: object
            required:
            - label
            - clicks
            - impressions
            properties:
              label:
                type: string
              clicks:
                type: integer
              impressions:
                type: integer
    PublicProofVisibilityMetric:
      type: object
      required:
      - value
      - raw_value
      properties:
        value:
          type: number
        raw_value:
          type: number
    PublicProofVerifiedRanking:
      type: object
      required:
      - keyword
      - position
      - clicks
      - impressions
      properties:
        keyword:
          type: string
        position:
          type: integer
        clicks:
          type: integer
        impressions:
          type: integer
    PublicProofVisibility:
      type: object
      nullable: true
      required:
      - captured_on
      - metrics
      - verified_rankings
      properties:
        captured_on:
          type: string
          format: date
          nullable: true
        metrics:
          type: object
          required:
          - visibility_index
          - share_of_voice_pct
          - estimated_traffic
          - estimated_traffic_value_usd
          - keywords_ranked
          - top3_count
          - top10_count
          properties:
            visibility_index:
              "$ref": "#/components/schemas/PublicProofVisibilityMetric"
            share_of_voice_pct:
              type: number
            estimated_traffic:
              type: integer
            estimated_traffic_value_usd:
              type: number
            keywords_ranked:
              type: integer
            top3_count:
              type: integer
            top10_count:
              type: integer
        verified_rankings:
          type: array
          items:
            "$ref": "#/components/schemas/PublicProofVerifiedRanking"
    PublicProofIssue:
      type: object
      required:
      - severity
      - issue_type
      - page_url
      - description
      properties:
        severity:
          type: string
        issue_type:
          type: string
        page_url:
          type: string
          nullable: true
        description:
          type: string
          nullable: true
    PublicProofResponse:
      type: object
      required:
      - proof
      - site
      - narrative
      - summary
      - scores
      - traffic
      - trust_growth_score
      - visibility
      - issues
      properties:
        proof:
          "$ref": "#/components/schemas/ProofLinks"
        site:
          "$ref": "#/components/schemas/PublicProofSite"
        narrative:
          "$ref": "#/components/schemas/PublicProofNarrative"
        summary:
          "$ref": "#/components/schemas/PublicProofSummary"
        scores:
          "$ref": "#/components/schemas/PublicProofScores"
        traffic:
          "$ref": "#/components/schemas/PublicProofTraffic"
        trust_growth_score:
          "$ref": "#/components/schemas/PublicProofTrustGrowthScore"
        visibility:
          "$ref": "#/components/schemas/PublicProofVisibility"
        issues:
          type: array
          items:
            "$ref": "#/components/schemas/PublicProofIssue"
    Meta:
      type: object
      required:
      - generated_at
      - plan
      - rate_limit
      properties:
        generated_at:
          type: string
          format: date-time
        plan:
          type: string
        rate_limit:
          type: object
          required:
          - limit
          - remaining
          - reset
          properties:
            limit:
              type: integer
            remaining:
              type: integer
              nullable: true
            reset:
              type: integer
        pagination:
          type: object
          nullable: true
          properties:
            page:
              type: integer
            per_page:
              type: integer
            total_count:
              type: integer
            total_pages:
              type: integer
        warnings:
          type: array
          items:
            type: string
        replayed:
          type: boolean
          nullable: true
          description: Present and true when a campaigns /commands or /submit_plan
            call was an idempotent replay of an already-recorded event.
    SiteSummary:
      type: object
      required:
      - slug
      - name
      - domain
      - overall_score
      - rank_eligible
      - score_date
      - created_at
      properties:
        slug:
          type: string
        name:
          type: string
        domain:
          type: string
        overall_score:
          type: integer
          nullable: true
        rank_eligible:
          type: boolean
        score_date:
          type: string
          format: date
          nullable: true
        created_at:
          type: string
          format: date-time
    SitesIndexResponse:
      type: object
      required:
      - data
      - meta
      properties:
        data:
          type: array
          items:
            "$ref": "#/components/schemas/SiteSummary"
        meta:
          "$ref": "#/components/schemas/Meta"
    ScoreValue:
      type: object
      required:
      - value
      - delta_7d
      - delta_30d
      properties:
        value:
          type: integer
          nullable: true
        delta_7d:
          type: integer
          nullable: true
        delta_30d:
          type: integer
          nullable: true
    ScoreUncertainty:
      type: object
      nullable: true
      properties:
        lower:
          type: number
          nullable: true
        upper:
          type: number
          nullable: true
      additionalProperties: true
    ScoreEvidence:
      type: object
      nullable: true
      required:
      - provisional
      - rank_eligible
      - score_state
      - champion_mode
      - computation_source
      properties:
        present:
          type: integer
          nullable: true
        total:
          type: integer
          nullable: true
        observed_weight:
          type: number
          nullable: true
        nominal_weight:
          type: number
          nullable: true
        confidence_weighted_coverage:
          type: number
          nullable: true
        coverage:
          type: number
          nullable: true
        provisional:
          type: boolean
          description: Whether the underlying score evidence remains provisional.
            Internal operator evidence only.
        incomplete:
          type: boolean
          nullable: true
        score_state:
          type: string
        rank_eligible:
          type: boolean
          description: Whether this published score may be used for public rank and
            tier.
        missing:
          type: array
          nullable: true
          items:
            type: object
            additionalProperties: true
        uncertainty:
          "$ref": "#/components/schemas/ScoreUncertainty"
        champion_mode:
          type: string
          enum:
          - validated
          - provisional
          - unavailable
          description: Internal publication lifecycle state for operator evidence.
        computation_source:
          type: string
      additionalProperties: false
    ScoreResponseData:
      type: object
      description: Values come from the current published TrustGrowth Score. Pillar
        fields are null when the score does not include that signal or exact-date
        evidence is unavailable.
      required:
      - date
      - overall_score
      - eeat
      - content_score
      - growth_score
      - technical_score
      - performance_score
      - authority_score
      - visibility_score
      - geo_score
      properties:
        date:
          type: string
          format: date
        overall_score:
          "$ref": "#/components/schemas/ScoreValue"
        eeat:
          type: object
          properties:
            score:
              "$ref": "#/components/schemas/ScoreValue"
            experience:
              "$ref": "#/components/schemas/ScoreValue"
            expertise:
              "$ref": "#/components/schemas/ScoreValue"
            authoritativeness:
              "$ref": "#/components/schemas/ScoreValue"
            trustworthiness:
              "$ref": "#/components/schemas/ScoreValue"
        content_score:
          "$ref": "#/components/schemas/ScoreValue"
        growth_score:
          "$ref": "#/components/schemas/ScoreValue"
        technical_score:
          "$ref": "#/components/schemas/ScoreValue"
        performance_score:
          "$ref": "#/components/schemas/ScoreValue"
        authority_score:
          "$ref": "#/components/schemas/ScoreValue"
        visibility_score:
          "$ref": "#/components/schemas/ScoreValue"
        geo_score:
          "$ref": "#/components/schemas/ScoreValue"
    ScoreResponse:
      type: object
      required:
      - data
      - meta
      properties:
        data:
          "$ref": "#/components/schemas/ScoreResponseData"
        meta:
          "$ref": "#/components/schemas/Meta"
    AuthorityMetric:
      type: object
      required:
      - value
      - source
      properties:
        value:
          type: number
          nullable: true
        source:
          type: string
          enum:
          - trustgrowth
          - dataforseo
        note:
          type: string
          nullable: true
        delta_7d:
          type: number
          nullable: true
        delta_30d:
          type: number
          nullable: true
    AuthorityResponseData:
      type: object
      required:
      - status
      - score
      - profile
      - freshness
      - eeat_link
      properties:
        status:
          type: string
          enum:
          - measured
          - stale
          - not_measured
        score:
          "$ref": "#/components/schemas/AuthorityMetric"
        profile:
          type: object
          nullable: true
          properties:
            referring_domains:
              "$ref": "#/components/schemas/AuthorityMetric"
            domain_rank:
              "$ref": "#/components/schemas/AuthorityMetric"
            broken_backlinks:
              "$ref": "#/components/schemas/AuthorityMetric"
            broken_backlink_sample:
              type: array
              nullable: true
              items:
                type: object
                properties:
                  url:
                    type: string
                    nullable: true
                  target:
                    type: string
                    nullable: true
                  first_seen:
                    type: string
                    format: date
                    nullable: true
        freshness:
          type: object
          required:
          - captured_on
          - stale
          properties:
            captured_on:
              type: string
              format: date
              nullable: true
            stale:
              type: boolean
        eeat_link:
          type: string
    AuthorityResponse:
      type: object
      required:
      - data
      - meta
      properties:
        data:
          "$ref": "#/components/schemas/AuthorityResponseData"
        meta:
          "$ref": "#/components/schemas/Meta"
    OperatorEeatBreakdown:
      type: object
      required:
      - score
      - experience
      - expertise
      - authoritativeness
      - trustworthiness
      properties:
        score:
          type: integer
          nullable: true
        experience:
          type: integer
          nullable: true
        expertise:
          type: integer
          nullable: true
        authoritativeness:
          type: integer
          nullable: true
        trustworthiness:
          type: integer
          nullable: true
    OperatorScoreBreakdown:
      type: object
      required:
      - overall_score
      - formula_version
      - formula_uid
      - formula_name
      - score_evidence
      - eeat
      - score_state
      - provisional
      - rank_eligible
      - computation_source
      - uncertainty
      - content_score
      - growth_score
      - technical_score
      - performance_score
      - authority_score
      - visibility_score
      - geo_score
      properties:
        overall_score:
          type: integer
          nullable: true
        formula_version:
          type: integer
          nullable: true
        formula_uid:
          type: string
          nullable: true
        formula_name:
          type: string
          nullable: true
        score_evidence:
          "$ref": "#/components/schemas/ScoreEvidence"
        score_state:
          type: string
          nullable: true
        provisional:
          type: boolean
        rank_eligible:
          type: boolean
        computation_source:
          type: string
          nullable: true
        uncertainty:
          "$ref": "#/components/schemas/ScoreUncertainty"
        eeat:
          "$ref": "#/components/schemas/OperatorEeatBreakdown"
        content_score:
          type: integer
          nullable: true
        growth_score:
          type: integer
          nullable: true
        technical_score:
          type: integer
          nullable: true
        performance_score:
          type: integer
          nullable: true
        authority_score:
          type: integer
          nullable: true
        visibility_score:
          type: integer
          nullable: true
        geo_score:
          type: integer
          nullable: true
    OperatorGscSnapshot:
      type: object
      required:
      - snapshot_date
      - clicks
      - impressions
      - ctr
      - position
      properties:
        snapshot_date:
          type: string
          format: date
          nullable: true
        clicks:
          type: integer
          nullable: true
        impressions:
          type: integer
          nullable: true
        ctr:
          type: number
          nullable: true
        position:
          type: number
          nullable: true
    OperatorContentPipeline:
      type: object
      required:
      - scheduled
      - in_progress
      - published
      - cycle_health
      properties:
        scheduled:
          type: integer
        in_progress:
          type: integer
        published:
          type: integer
        cycle_health:
          type: object
          required:
          - current_cycle
          - triage_needed
          - cleanup
          properties:
            current_cycle:
              type: integer
            triage_needed:
              type: integer
            cleanup:
              type: integer
    OperatorGeneratedTimestamps:
      type: object
      required:
      - packet_generated_at
      - latest_score_date
      - latest_snapshot_date
      - latest_audit_at
      properties:
        packet_generated_at:
          type: string
          format: date-time
        latest_score_date:
          type: string
          format: date
          nullable: true
        latest_snapshot_date:
          type: string
          format: date
          nullable: true
        latest_audit_at:
          type: string
          format: date-time
          nullable: true
    OperatorSummarySource:
      type: object
      required:
      - surface
      - sections
      properties:
        surface:
          type: string
        sections:
          type: array
          items:
            type: string
    OperatorSummary:
      type: object
      required:
      - overall_score
      - eeat
      - content_score
      - growth_score
      - technical_score
      - performance_score
      - authority_score
      - visibility_score
      - geo_score
      - score_date
      - score_breakdown
      - gsc_snapshot
      - content_pipeline
      - generated_timestamps
      - source
      properties:
        overall_score:
          type: integer
          nullable: true
        eeat:
          "$ref": "#/components/schemas/OperatorEeatBreakdown"
        content_score:
          type: integer
          nullable: true
        growth_score:
          type: integer
          nullable: true
        technical_score:
          type: integer
          nullable: true
        performance_score:
          type: integer
          nullable: true
        authority_score:
          type: integer
          nullable: true
        visibility_score:
          type: integer
          nullable: true
        geo_score:
          type: integer
          nullable: true
        score_date:
          type: string
          format: date
          nullable: true
        score_breakdown:
          "$ref": "#/components/schemas/OperatorScoreBreakdown"
        gsc_snapshot:
          "$ref": "#/components/schemas/OperatorGscSnapshot"
        content_pipeline:
          "$ref": "#/components/schemas/OperatorContentPipeline"
        generated_timestamps:
          "$ref": "#/components/schemas/OperatorGeneratedTimestamps"
        source:
          "$ref": "#/components/schemas/OperatorSummarySource"
    KeywordOpportunity:
      type: object
      required:
      - keyword
      - volume
      - difficulty
      - current_position
      - opportunity_score
      - variant_count
      - combined_volume
      properties:
        keyword:
          type: string
        source:
          type: string
        type:
          type: string
        intent:
          type: string
          nullable: true
        funnel_stage:
          type: string
          nullable: true
        cpc:
          type: number
          nullable: true
        provenance:
          type: object
          additionalProperties:
            type: string
            enum:
            - measured
            - estimated
            nullable: true
          description: measured = DataForSEO; estimated = regex or derived. Null means
            the metric is absent.
        volume:
          type: integer
          nullable: true
        difficulty:
          type: integer
          nullable: true
        current_position:
          type: integer
          nullable: true
        opportunity_score:
          type: number
          nullable: true
        owning_url:
          type: string
          nullable: true
        action:
          type: string
          nullable: true
          enum:
          - update_page
          - consolidate
          - new_page
        geo:
          type: object
          nullable: true
          properties:
            shape:
              type: string
            trust_task_first:
              type: boolean
            ai_overview_present:
              type: boolean
              nullable: true
        rationale:
          type: string
          nullable: true
        serp_features:
          type: array
          items:
            type: string
          description: Empty is unknown, not no features.
        variant_count:
          type: integer
          description: Near-duplicate phrasings collapsed under this row. 0 means
            the row stands alone.
        combined_volume:
          type: integer
          nullable: true
          description: Search volume of this row plus its variants. Null when no row
            in the group has a volume — unknown demand, not zero.
    KeywordListResponse:
      type: object
      required:
      - data
      - meta
      properties:
        data:
          type: array
          items:
            "$ref": "#/components/schemas/KeywordOpportunity"
        meta:
          "$ref": "#/components/schemas/Meta"
    Competitor:
      type: object
      required:
      - domain
      - overlap_percentage
      - shared_keywords_count
      - their_avg_position
      - source
      properties:
        domain:
          type: string
        overlap_percentage:
          type: integer
        shared_keywords_count:
          type: integer
        their_avg_position:
          type: number
          nullable: true
        source:
          type: string
    CompetitorListResponse:
      type: object
      required:
      - data
      - meta
      properties:
        data:
          type: array
          items:
            "$ref": "#/components/schemas/Competitor"
        meta:
          "$ref": "#/components/schemas/Meta"
    CampaignWorkItemCounts:
      type: object
      required:
      - total
      - by_planning_status
      properties:
        total:
          type: integer
        by_planning_status:
          type: object
          additionalProperties:
            type: integer
          description: Raw count per CampaignWorkItem::PLANNING_STATUSES value present
            on the campaign.
    CampaignSummary:
      type: object
      required:
      - uid
      - key
      - name
      - kind
      - status
      - anchor_on
      - starts_on
      - ends_on
      - timezone
      - work_item_counts
      properties:
        uid:
          type: string
        key:
          type: string
        name:
          type: string
        kind:
          type: string
          enum:
          - launch
          - event
          - seasonal
          - announcement
          - series
        status:
          type: string
          enum:
          - draft
          - planning
          - pending_review
          - active
          - completed
          - cancelled
        anchor_on:
          type: string
          format: date
          nullable: true
        starts_on:
          type: string
          format: date
          nullable: true
        ends_on:
          type: string
          format: date
          nullable: true
        timezone:
          type: string
        work_item_counts:
          "$ref": "#/components/schemas/CampaignWorkItemCounts"
    CampaignsIndexResponse:
      type: object
      required:
      - data
      - meta
      properties:
        data:
          type: array
          items:
            "$ref": "#/components/schemas/CampaignSummary"
        meta:
          "$ref": "#/components/schemas/Meta"
    CampaignExecutionState:
      type: object
      description: Projected read model from Campaigns::WorkItemExecutionState — never
        persisted.
      required:
      - state
      - blocked_reason
      - transient_activity
      properties:
        state:
          type: string
          enum:
          - planned
          - briefing
          - drafting
          - in_review
          - revision_needed
          - packaged
          - awaiting_gate
          - scheduled
          - publishing
          - published
          - strategy_required
          - blocked
          - cancelled
        blocked_reason:
          type: string
          nullable: true
        transient_activity:
          type: boolean
    CampaignWorkItem:
      type: object
      required:
      - uid
      - title
      - item_type
      - layer
      - lane
      - keyword_opportunity_id
      - planning_status
      - hold_state
      - lock_state
      - execution_state
      properties:
        uid:
          type: string
        title:
          type: string
        item_type:
          type: string
          enum:
          - content_piece
          - distribution_action
          - manual_action
          - operational_milestone
          - external_task_reference
        layer:
          type: string
          nullable: true
        lane:
          type: string
          enum:
          - seo
          - brand
          nullable: true
        keyword_opportunity_id:
          type: integer
          nullable: true
        planning_status:
          type: string
          enum:
          - proposed
          - discussing
          - approved
          - cancelled
        hold_state:
          type: string
          enum:
          - clear
          - held
        lock_state:
          type: string
          enum:
          - unlocked
          - locked
        execution_state:
          "$ref": "#/components/schemas/CampaignExecutionState"
    CampaignDetail:
      type: object
      description: CampaignSummary fields plus positioning and the hypothesis block
        (design doc §3).
      required:
      - uid
      - key
      - name
      - kind
      - status
      - anchor_on
      - starts_on
      - ends_on
      - timezone
      - work_item_counts
      - positioning
      - hypothesis
      - opportunity
      - intended_behavior
      - audience
      - evidence_basis
      - baseline
      - work_items
      properties:
        uid:
          type: string
        key:
          type: string
        name:
          type: string
        kind:
          type: string
          enum:
          - launch
          - event
          - seasonal
          - announcement
          - series
        status:
          type: string
          enum:
          - draft
          - planning
          - pending_review
          - active
          - completed
          - cancelled
        anchor_on:
          type: string
          format: date
          nullable: true
        starts_on:
          type: string
          format: date
          nullable: true
        ends_on:
          type: string
          format: date
          nullable: true
        timezone:
          type: string
        work_item_counts:
          "$ref": "#/components/schemas/CampaignWorkItemCounts"
        positioning:
          type: string
          nullable: true
        hypothesis:
          type: string
          nullable: true
          description: Falsifiable hypothesis statement.
        opportunity:
          type: string
          nullable: true
        intended_behavior:
          type: string
          nullable: true
        audience:
          type: string
          nullable: true
        evidence_basis:
          type: string
          nullable: true
        baseline:
          type: object
          additionalProperties: true
        work_items:
          type: array
          items:
            "$ref": "#/components/schemas/CampaignWorkItem"
    CampaignDetailResponse:
      type: object
      required:
      - data
      - meta
      properties:
        data:
          "$ref": "#/components/schemas/CampaignDetail"
        meta:
          "$ref": "#/components/schemas/Meta"
    CampaignOwner:
      type: object
      description: 'Resolution order: assignee_user_id -> agent_key -> owner_label
        -> owner_role.'
      required:
      - kind
      - value
      properties:
        kind:
          type: string
          nullable: true
          enum:
          - user
          - agent
          - label
          - role
        value:
          type: string
          nullable: true
    CampaignGate:
      type: object
      description: The single live gate — computed, never persisted (design principle
        5).
      required:
      - kind
      - item_uid
      - description
      - owner
      properties:
        kind:
          type: string
          enum:
          - blocked
          - review
          - strategy_required
        item_uid:
          type: string
        description:
          type: string
        owner:
          "$ref": "#/components/schemas/CampaignOwner"
    CampaignWaitingOnYouEntry:
      type: object
      required:
      - item_uid
      - review_type
      - requested_at
      properties:
        item_uid:
          type: string
        review_type:
          type: string
        requested_at:
          type: string
          format: date-time
    CampaignMissionControlItem:
      type: object
      required:
      - uid
      - title
      - item_type
      - layer
      - lane
      - keyword_opportunity_id
      - planning_status
      - hold_state
      - lock_state
      - execution_state
      - owner
      properties:
        uid:
          type: string
        title:
          type: string
        item_type:
          type: string
          enum:
          - content_piece
          - distribution_action
          - manual_action
          - operational_milestone
          - external_task_reference
        layer:
          type: string
          nullable: true
        lane:
          type: string
          enum:
          - seo
          - brand
          nullable: true
        keyword_opportunity_id:
          type: integer
          nullable: true
        planning_status:
          type: string
          enum:
          - proposed
          - discussing
          - approved
          - cancelled
        hold_state:
          type: string
          enum:
          - clear
          - held
        lock_state:
          type: string
          enum:
          - unlocked
          - locked
        execution_state:
          "$ref": "#/components/schemas/CampaignExecutionState"
        owner:
          "$ref": "#/components/schemas/CampaignOwner"
    CampaignAtRiskFinding:
      type: object
      description: One Campaigns::AtRiskPolicy finding (slice-6 policy 7) — pure,
        computed fresh on every mission-control read, never persisted.
      required:
      - constraint_uid
      - work_item_uid
      - milestone
      - reason
      - detail
      properties:
        constraint_uid:
          type: string
        work_item_uid:
          type: string
        milestone:
          type: string
          enum:
          - brief_due
          - draft_due
          - asset_due
          - package_due
          - publish
          - distribution
        reason:
          type: string
          enum:
          - runway
          - overdue_artifact
          - held
          - unresolvable_near_end
          description: '"runway" (unresolved, latest permissible date within DEFAULT_RUNWAY_DAYS),
            "overdue_artifact" (resolved past as_of with the milestone''s expected
            artifact absent), "held" (work item held with an unresolved condition
            constraint), or "unresolvable_near_end" (unresolved with no computable
            window at all — anchor_missing/predecessor_unpublished/condition_held
            — and the campaign''s own ends_on is within DEFAULT_RUNWAY_DAYS).'
        detail:
          type: string
    CampaignRuleSummary:
      type: object
      description: A campaign rule's current enforced state — CampaignRule#current_version
        projected to rule_key/enforcement_mode/version. Authority always lives on
        the versioned CampaignRuleVersion row (design §3, Codex amendment P7); this
        is the read-side "what does this rule enforce right now" convenience.
      required:
      - rule_key
      - enforcement_mode
      - version
      properties:
        rule_key:
          type: string
        enforcement_mode:
          type: string
          enum:
          - deterministic
          - reviewer
          - human
          - advisory
        version:
          type: integer
          description: The current version number (1-based, monotonically increasing
            per rule).
    CampaignHumanRule:
      type: object
      description: 'One human-mode campaign rule surfaced as review-queue gate context
        (slice 7 task 4, design §3 policy 5: "human rules map to review-queue gates"
        — V1 adds this as context on the existing review-queue item, not a new review
        type). Current version only — CampaignRuleVersion is the authority (Codex
        amendment P7).'
      required:
      - rule_key
      - version
      - body
      properties:
        rule_key:
          type: string
        version:
          type: integer
          description: The current version number (1-based, monotonically increasing
            per rule).
        body:
          type: string
    CampaignReReviewFinding:
      type: object
      description: One Campaigns::ReReviewProjection finding (Codex P8, slice-7 policy
        8) — a campaign content artifact (draft or package) whose most recent review
        snapshot does not yet include a deterministic/reviewer rule's current version.
        Computed fresh on every read, never persisted; resolves itself once a later
        approval snapshots the superseding version. V1 does not auto-enqueue re-reviews.
      required:
      - artifact_uid
      - artifact_type
      - reviewed_version_ids
      - superseding_version_id
      properties:
        artifact_uid:
          type: string
        artifact_type:
          type: string
          enum:
          - ContentDraft
          - ContentPackage
        reviewed_version_ids:
          type: array
          items:
            type: integer
          description: 'CampaignRuleVersion ids captured in this artifact''s most
            recent review snapshot. Empty when no snapshot exists yet (fails safe:
            an absent snapshot on a campaign with deterministic/reviewer rules always
            counts as affected).'
        superseding_version_id:
          type: integer
          description: The rule's current CampaignRuleVersion id, not yet present
            in reviewed_version_ids.
    CampaignMissionControl:
      type: object
      required:
      - single_live_gate
      - waiting_on_you
      - counts
      - items
      - at_risk
      - rules
      - re_review_required
      properties:
        single_live_gate:
          allOf:
          - "$ref": "#/components/schemas/CampaignGate"
          nullable: true
        waiting_on_you:
          type: array
          items:
            "$ref": "#/components/schemas/CampaignWaitingOnYouEntry"
        counts:
          type: object
          additionalProperties:
            type: integer
          description: Item count per Campaigns::WorkItemExecutionState::STATES value
            present on the campaign.
        items:
          type: array
          items:
            "$ref": "#/components/schemas/CampaignMissionControlItem"
        at_risk:
          type: array
          items:
            "$ref": "#/components/schemas/CampaignAtRiskFinding"
        rules:
          type: array
          items:
            "$ref": "#/components/schemas/CampaignRuleSummary"
          description: Every campaign rule with a current version, per Campaigns::MissionControlSnapshot#rules
            (slice 7 task 2).
        re_review_required:
          type: array
          items:
            "$ref": "#/components/schemas/CampaignReReviewFinding"
          description: Live Campaigns::ReReviewProjection sweep across every deterministic/reviewer
            rule the campaign has (slice 7 task 2).
    CampaignMissionControlResponse:
      type: object
      required:
      - data
      - meta
      properties:
        data:
          "$ref": "#/components/schemas/CampaignMissionControl"
        meta:
          "$ref": "#/components/schemas/Meta"
    GrowthCycle:
      type: object
      required:
      - type
      - uid
      - status
      - starts_on
      - ends_on
      - calendar_entry_count
      - baseline_metrics
      - ending_metrics
      properties:
        type:
          type: string
          enum:
          - growth_cycle
        uid:
          type: string
        status:
          type: string
        starts_on:
          type: string
          format: date
        ends_on:
          type: string
          format: date
        approved_at:
          type: string
          format: date-time
          nullable: true
        previous_cycle_uid:
          type: string
          nullable: true
        strategy_uid:
          type: string
          nullable: true
        calendar_entry_count:
          type: integer
        baseline_metrics:
          type: object
          additionalProperties: true
        ending_metrics:
          type: object
          additionalProperties: true
        strategy_review_summary:
          type: string
          nullable: true
        content_results:
          type: object
          additionalProperties: true
        publication_results:
          type: object
          additionalProperties: true
        lessons_learned:
          type: object
          additionalProperties: true
        next_strategy_inputs:
          type: object
          additionalProperties: true
    GrowthCycleResponse:
      type: object
      required:
      - data
      - meta
      properties:
        data:
          "$ref": "#/components/schemas/GrowthCycle"
        meta:
          "$ref": "#/components/schemas/Meta"
    CampaignCreateRequest:
      type: object
      required:
      - key
      - name
      - kind
      properties:
        key:
          type: string
          description: Lowercase slug, unique per site (letters, numbers, hyphens).
        name:
          type: string
        kind:
          type: string
          enum:
          - launch
          - event
          - seasonal
          - announcement
          - series
        anchor_on:
          type: string
          format: date
          nullable: true
        starts_on:
          type: string
          format: date
          nullable: true
        ends_on:
          type: string
          format: date
          nullable: true
        timezone:
          type: string
          description: IANA timezone identifier. Defaults to UTC.
        positioning:
          type: string
          nullable: true
        audience:
          type: string
          nullable: true
        opportunity:
          type: string
          nullable: true
        intended_behavior:
          type: string
          nullable: true
        evidence_basis:
          type: string
          nullable: true
        hypothesis:
          type: string
          nullable: true
        baseline:
          type: object
          additionalProperties: true
          nullable: true
    CampaignCreateResponseData:
      type: object
      required:
      - operation
      - message
      - site_slug
      - resource
      - next_step
      properties:
        operation:
          type: string
          enum:
          - create_campaign
        message:
          type: string
        site_slug:
          type: string
        resource:
          "$ref": "#/components/schemas/CampaignDetail"
        next_step:
          "$ref": "#/components/schemas/WorkflowNextStep"
    CampaignCreateResponse:
      type: object
      required:
      - data
      - meta
      properties:
        data:
          "$ref": "#/components/schemas/CampaignCreateResponseData"
        meta:
          "$ref": "#/components/schemas/Meta"
    CampaignCommandRequest:
      type: object
      description: See Campaigns::Commands::Execute (app/services/campaigns/commands/execute.rb)
        for the full precondition/locking/idempotency contract this endpoint is a
        thin envelope over.
      required:
      - command
      - idempotency_key
      properties:
        command:
          type: string
          enum:
          - propose_item
          - approve_item
          - cancel_item
          - hold
          - release_hold
          - lock_item
          - unlock_item
          - lock_schedule
          - unlock_schedule
          - create_schedule_constraint
          - reschedule
          - replace_evidence
          - request_changes
          - regenerate_draft
          - rebuild_package
          - change_owner
          - amend_item_lane
          - add_rule
          - amend_rule
          - split_lane
          - materialize
          - amend_plan_field
          - submit_plan_revision
          - reserve_capacity
          - release_capacity
          - grant_exception
          - add_dependency
          - remove_dependency
          - resolve_schedule
          - add_asset_requirement
          - remove_asset_requirement
          - complete_campaign
          - add_metric_definition
          - link_existing_entry
          description: create_campaign and external_publication_recorded are excluded
            — create_campaign is written directly by POST /campaigns, and external_publication_recorded
            is a system event written by the publication wakeup path; neither executes
            through this endpoint (both return not_executable_here if attempted).
        idempotency_key:
          type: string
          description: Identifies the request, not the command — replaying the same
            key (even with a different command) returns the original event with meta.replayed=true
            and does not re-execute.
        params:
          type: object
          additionalProperties: true
          nullable: true
          description: Command-specific fields validated per command. propose_item
            accepts optional lane (seo or brand) and keyword_opportunity_id; amend_item_lane
            accepts lane and optional keyword_opportunity_id. create_schedule_constraint
            requires work_item_uid, milestone, and kind (fixed or anchor_offset),
            plus the matching date or offset window and optional IANA timezone; it
            is human-only and immediately submits a new immutable plan revision for
            campaign_plan_approval.
        rationale:
          type: string
          nullable: true
        expected_lock_version:
          type: integer
          nullable: true
          description: Optimistic-lock check against the target work item's lock_version.
            A mismatch (or an item modified since) returns 409 stale_object.
        source_ref:
          type: object
          additionalProperties: true
          nullable: true
    CampaignEventResource:
      type: object
      required:
      - sequence
      - command
      - actor_type
      - actor_id
      - idempotency_key
      - old_value
      - new_value
      - created_at
      properties:
        sequence:
          type: integer
        command:
          type: string
          enum:
          - propose_item
          - approve_item
          - cancel_item
          - hold
          - release_hold
          - lock_item
          - unlock_item
          - lock_schedule
          - unlock_schedule
          - create_schedule_constraint
          - reschedule
          - replace_evidence
          - request_changes
          - regenerate_draft
          - rebuild_package
          - change_owner
          - amend_item_lane
          - add_rule
          - amend_rule
          - split_lane
          - materialize
          - amend_plan_field
          - submit_plan_revision
          - reserve_capacity
          - release_capacity
          - grant_exception
          - add_dependency
          - remove_dependency
          - resolve_schedule
          - add_asset_requirement
          - remove_asset_requirement
          - complete_campaign
          - add_metric_definition
          - link_existing_entry
          - create_campaign
          - external_publication_recorded
        actor_type:
          type: string
          enum:
          - user
          - agent
        actor_id:
          type: string
        idempotency_key:
          type: string
        campaign_work_item_uid:
          type: string
          nullable: true
        old_value:
          type: object
          additionalProperties: true
        new_value:
          type: object
          additionalProperties: true
        rationale:
          type: string
          nullable: true
        created_at:
          type: string
          format: date-time
    CampaignCommandResponseData:
      type: object
      required:
      - operation
      - message
      - site_slug
      - resource
      - next_step
      properties:
        operation:
          type: string
          enum:
          - propose_item
          - approve_item
          - cancel_item
          - hold
          - release_hold
          - lock_item
          - unlock_item
          - lock_schedule
          - unlock_schedule
          - create_schedule_constraint
          - reschedule
          - replace_evidence
          - request_changes
          - regenerate_draft
          - rebuild_package
          - change_owner
          - amend_item_lane
          - add_rule
          - amend_rule
          - split_lane
          - materialize
          - amend_plan_field
          - submit_plan_revision
          - reserve_capacity
          - release_capacity
          - grant_exception
          - add_dependency
          - remove_dependency
          - resolve_schedule
          - add_asset_requirement
          - remove_asset_requirement
          - complete_campaign
          - add_metric_definition
          - link_existing_entry
          - create_campaign
          - external_publication_recorded
        message:
          type: string
        site_slug:
          type: string
        resource:
          "$ref": "#/components/schemas/CampaignEventResource"
        next_step:
          "$ref": "#/components/schemas/WorkflowNextStep"
        warnings:
          type: array
          items:
            "$ref": "#/components/schemas/ContentLaneWarning"
          description: Present when amend_item_lane succeeds but a linked brief or
            draft was generated under the previous lane. Regeneration is operator-chosen.
    CampaignCommandResponse:
      type: object
      required:
      - data
      - meta
      properties:
        data:
          "$ref": "#/components/schemas/CampaignCommandResponseData"
        meta:
          "$ref": "#/components/schemas/Meta"
    CampaignSubmitPlanRequest:
      type: object
      required:
      - idempotency_key
      properties:
        idempotency_key:
          type: string
        rationale:
          type: string
          nullable: true
        source_ref:
          type: object
          additionalProperties: true
          nullable: true
    CampaignSubmitPlanResource:
      type: object
      required:
      - campaign_uid
      properties:
        campaign_uid:
          type: string
        revision_uid:
          type: string
          nullable: true
        revision_version:
          type: integer
          nullable: true
        review_queue_item_uid:
          type: string
          nullable: true
    CampaignSubmitPlanResponseData:
      type: object
      required:
      - operation
      - message
      - site_slug
      - resource
      - next_step
      properties:
        operation:
          type: string
          enum:
          - submit_plan_revision
        message:
          type: string
        site_slug:
          type: string
        resource:
          "$ref": "#/components/schemas/CampaignSubmitPlanResource"
        next_step:
          "$ref": "#/components/schemas/WorkflowNextStep"
    CampaignSubmitPlanResponse:
      type: object
      required:
      - data
      - meta
      properties:
        data:
          "$ref": "#/components/schemas/CampaignSubmitPlanResponseData"
        meta:
          "$ref": "#/components/schemas/Meta"
    CampaignMetricObservationRequest:
      type: object
      description: See Campaigns::RecordMetricObservation (app/services/campaigns/record_metric_observation.rb)
        for the full upsert/concurrency contract. NOT an idempotency_key-bearing command
        request — the (metric_definition_uid, window, source_ref) triple itself is
        the idempotency key.
      required:
      - metric_definition_uid
      - window
      - source_ref
      - observed_at
      properties:
        metric_definition_uid:
          type: string
          description: A CampaignMetricDefinition uid belonging to this campaign (created
            via add_metric_definition on POST .../commands).
        window:
          type: integer
          enum:
          - 3
          - 7
          - 14
          - 28
          description: Must be one of the definition's own configured windows when
            it declares any.
        source_ref:
          type: string
          description: Non-blank collector/source identifier — the third leg of the
            (definition, window, source_ref) idempotency key. Repeating the same triple
            upserts this same row.
        observed_at:
          type: string
          format: date-time
          description: ISO8601 timestamp of the underlying measurement.
        numeric_value:
          type: number
          nullable: true
          description: Required (and the ONLY populated value field) when the definition's
            value_type is count/rate/score/duration.
        text_value:
          type: string
          nullable: true
          description: Permanently unused in V1 — no CampaignMetricDefinition::VALUE_TYPES
            member maps to it; populating it is a 422 invalid_params.
        structured_value:
          type: object
          additionalProperties: true
          nullable: true
          description: Required (and the ONLY populated value field) when the definition's
            value_type is snapshot.
        unit:
          type: string
          nullable: true
        baseline_value:
          type: number
          nullable: true
        attribution_context:
          type: object
          additionalProperties: true
          nullable: true
          description: Free-form — a caller may stamp the campaign's own attribution_key
            here for a future collector join (no join is enforced in V1).
        collection_status:
          type: string
          enum:
          - recorded
          - partial
          - stale
          nullable: true
          description: Defaults to "recorded" when omitted.
    CampaignMetricObservationResource:
      type: object
      required:
      - uid
      - metric_definition_uid
      - window
      - observed_at
      - source_ref
      - collection_status
      properties:
        uid:
          type: string
        metric_definition_uid:
          type: string
        window:
          type: integer
        observed_at:
          type: string
          format: date-time
        numeric_value:
          type: number
          nullable: true
        text_value:
          type: string
          nullable: true
        structured_value:
          type: object
          additionalProperties: true
          nullable: true
        unit:
          type: string
          nullable: true
        source_ref:
          type: string
        baseline_value:
          type: number
          nullable: true
        attribution_context:
          type: object
          additionalProperties: true
        collection_status:
          type: string
          enum:
          - recorded
          - partial
          - stale
    CampaignMetricObservationResponseData:
      type: object
      required:
      - operation
      - message
      - site_slug
      - resource
      properties:
        operation:
          type: string
          enum:
          - record_metric_observation
        message:
          type: string
        site_slug:
          type: string
        resource:
          "$ref": "#/components/schemas/CampaignMetricObservationResource"
    CampaignMetricObservationResponse:
      type: object
      required:
      - data
      - meta
      properties:
        data:
          "$ref": "#/components/schemas/CampaignMetricObservationResponseData"
        meta:
          "$ref": "#/components/schemas/Meta"
    CampaignPlanningSessionTranscriptMessage:
      type: object
      description: One Message row from the session's Chat (RubyLLM's Chat/Message
        infrastructure — no bespoke transcript store). A failed or skipped turn (CampaignPlanningAgent#append_turn_message!)
        appears here like any other assistant message — role "assistant", human-readable
        `content` — distinguishable from an ordinary reply via `kind`.
      required:
      - role
      - content
      - created_at
      - kind
      properties:
        role:
          type: string
        content:
          type: string
          nullable: true
        created_at:
          type: string
          format: date-time
        kind:
          type: string
          nullable: true
          enum:
          - campaign_planning_turn_failed
          - campaign_planning_turn_skipped
          -
          description: Non-null only for a turn-status message CampaignPlanningAgent
            appended on failure/skip — never set on a user message or an ordinary
            successful assistant reply.
    CampaignPlanningSession:
      type: object
      required:
      - uid
      - campaign_uid
      - status
      - started_by_type
      - started_by_id
      - created_at
      - updated_at
      properties:
        uid:
          type: string
        campaign_uid:
          type: string
        status:
          type: string
          enum:
          - active
          - ended
        started_by_type:
          type: string
          enum:
          - user
          - agent
        started_by_id:
          type: string
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time
        transcript:
          type: array
          items:
            "$ref": "#/components/schemas/CampaignPlanningSessionTranscriptMessage"
          description: Only present on GET /planning_sessions/{session_uid} — every
            Message on the session's Chat, chronological, never truncated.
    CampaignPlanningSessionResponse:
      type: object
      required:
      - data
      - meta
      properties:
        data:
          "$ref": "#/components/schemas/CampaignPlanningSession"
        meta:
          "$ref": "#/components/schemas/Meta"
    CampaignPlanningSessionMessageRequest:
      type: object
      required:
      - content
      properties:
        content:
          type: string
          description: The user's message to the campaign planner. Must not be blank.
    CampaignPlanningSessionMessageResponseData:
      type: object
      required:
      - operation
      - message
      - session_uid
      - message_id
      - run_uid
      - next_step
      properties:
        operation:
          type: string
          enum:
          - planning_session_message
        message:
          type: string
        session_uid:
          type: string
        message_id:
          type: integer
        run_uid:
          type: string
          description: The queued CampaignPlanningAgent AgentRun's uid — the run reference
            for this turn.
        next_step:
          "$ref": "#/components/schemas/WorkflowNextStep"
    CampaignPlanningSessionMessageResponse:
      type: object
      required:
      - data
      - meta
      properties:
        data:
          "$ref": "#/components/schemas/CampaignPlanningSessionMessageResponseData"
        meta:
          "$ref": "#/components/schemas/Meta"
    ContentItem:
      type: object
      required:
      - uid
      - title
      - content_type
      - status
      - scheduled_date
      - campaign_work_item_uid
      - published_date
      - brief_status
      - draft_status
      - word_count
      - triage_needed
      - stale_for_triage
      - automation_ready
      - current_cycle
      properties:
        uid:
          type: string
        title:
          type: string
        content_type:
          type: string
        status:
          type: string
        growth_cycle_uid:
          type: string
          nullable: true
        scheduled_date:
          type: string
          format: date
          nullable: true
        target_keyword:
          type: string
          nullable: true
        keyword_opportunity_id:
          type: integer
          nullable: true
          description: Linked site keyword opportunity ID; null when target_keyword
            is blank or untracked.
        campaign_work_item_uid:
          type: string
          nullable: true
          readOnly: true
          description: Authoritative campaign linkage for campaign classification;
            null for ordinary strategy work. schedule_intent is legacy metadata and
            must not be used for classification.
        funnel_stage:
          type: string
          nullable: true
        pillar:
          type: string
          nullable: true
        target_word_count:
          type: integer
          nullable: true
        priority:
          type: integer
          nullable: true
        platform:
          type: string
          nullable: true
        topic:
          type: string
          nullable: true
        channel:
          type: string
          nullable: true
        schedule_intent:
          type: string
          nullable: true
          readOnly: true
          deprecated: true
          description: Read-only legacy migration evidence. Never use for campaign
            classification; use campaign_work_item_uid.
        owner_notes:
          type: string
          nullable: true
        claim_safety_notes:
          type: string
          nullable: true
        import_batch_uid:
          type: string
          nullable: true
        published_date:
          type: string
          format: date-time
          nullable: true
        triage_state:
          type: string
          nullable: true
        triage_reason:
          type: string
          nullable: true
        effective_triage_state:
          type: string
          nullable: true
        triage_needed:
          type: boolean
        stale_for_triage:
          type: boolean
        triage_reference_at:
          type: string
          format: date-time
        automation_ready:
          type: boolean
        current_cycle:
          type: boolean
        archived_at:
          type: string
          format: date-time
          nullable: true
        archived_by:
          type: string
          nullable: true
          description: Actor that archived this entry, e.g. `user:<uid>`. Null for
            rows archived before archive provenance existed, or for an unattributed
            system path.
        archived_reason:
          type: string
          nullable: true
          description: 'One of: strategy_superseded, calendar_import_replaced, triage_archive,
            triage_delete_candidate, brief_rejected.'
        pre_archive_status:
          type: string
          nullable: true
          description: Pipeline status the entry held immediately before it was archived;
            the status POST /restore returns it to.
        restored_from_archive:
          type: object
          nullable: true
          additionalProperties: true
          description: 'Provenance of the last restore: when, by whom, and the archive
            record it reversed.'
        brief_status:
          type: string
          nullable: true
        draft_status:
          type: string
          nullable: true
        schema_type_candidate:
          type: string
          nullable: true
          enum:
          - article
          - faq
          - how_to
        package_status:
          type: string
          nullable: true
        package_claim_risk_level:
          type: string
          nullable: true
          enum:
          - low
          - medium
          - high
        package_review_status:
          type: string
          nullable: true
        package_execution_state:
          type: string
          nullable: true
        draft_review:
          "$ref": "#/components/schemas/DraftReviewState"
        publication_state:
          type: string
          nullable: true
        publication_verified:
          type: boolean
          nullable: true
        legacy_unverified:
          type: boolean
          nullable: true
        external_url:
          type: string
          nullable: true
        social_engagement:
          "$ref": "#/components/schemas/SocialEngagement"
        word_count:
          type: integer
        warnings:
          type: array
          items:
            "$ref": "#/components/schemas/ContentLaneWarning"
          description: Present when a stored brief or draft was generated under a
            different lane than the entry's current lane. Regeneration is operator-chosen.
    ContentLaneWarning:
      type: object
      required:
      - code
      - generated_under_lane
      - current_lane
      additionalProperties: false
      properties:
        code:
          type: string
          enum:
          - brief_generated_under_previous_lane
          - draft_generated_under_previous_lane
        brief_uid:
          type: string
        draft_uid:
          type: string
        generated_under_lane:
          type: string
          enum:
          - seo
          - brand
        current_lane:
          type: string
          enum:
          - seo
          - brand
    SocialEngagementMetric:
      type: object
      required:
      - status
      - value
      - reason
      properties:
        status:
          type: string
          enum:
          - available
          - unavailable
        value:
          type: number
          nullable: true
          description: Persisted native value. Numeric zero is available evidence.
        reason:
          type: string
          nullable: true
          description: Non-empty machine-readable reason when status is unavailable.
      additionalProperties: false
    SocialEngagement:
      type: object
      nullable: true
      required:
      - refreshed_at
      - platform
      - metrics
      description: Normalized native engagement from the latest verified social publication.
        Stored likes/comments/shares aliases are returned as reactions/replies/reposts;
        values are never estimated.
      properties:
        refreshed_at:
          type: string
          format: date-time
          nullable: true
        platform:
          type: string
          enum:
          - x
          - linkedin
        metrics:
          type: object
          required:
          - impressions
          - reactions
          - replies
          - reposts
          - follower_trend
          properties:
            impressions:
              "$ref": "#/components/schemas/SocialEngagementMetric"
            reactions:
              "$ref": "#/components/schemas/SocialEngagementMetric"
            replies:
              "$ref": "#/components/schemas/SocialEngagementMetric"
            reposts:
              "$ref": "#/components/schemas/SocialEngagementMetric"
            follower_trend:
              "$ref": "#/components/schemas/SocialEngagementMetric"
          additionalProperties: false
      additionalProperties: false
    SocialEngagementUpsertRequest:
      type: object
      required:
      - platform
      description: Either a metrics capture (refreshed_at + metrics) or a terminal
        unavailable record (status + measurement_window + unavailable). Missing provider
        metrics are never recorded as zeros.
      oneOf:
      - required:
        - refreshed_at
        - metrics
        not:
          required:
          - status
      - required:
        - status
        - measurement_window
        - unavailable
      properties:
        platform:
          type: string
          enum:
          - x
          - linkedin
        publication_uid:
          type: string
          description: Publication this write targets, echoed from the measurement
            queue task. Required whenever an entry holds more than one published record
            for the platform; omitting it targets the newest.
        status:
          type: string
          enum:
          - analytics_unavailable
          - not_postiz_measurable
          description: Marks this write as a terminal quarantine record instead of
            an engagement snapshot.
        measurement_window:
          type: string
          enum:
          - 24h
          - 72h
          - 7d
          description: Optional checkpoint identity. When present, stores this snapshot
            independently and removes only that checkpoint from the measurement queue.
            Required for a terminal unavailable record.
        refreshed_at:
          type: string
          format: date-time
        metrics:
          type: object
          minProperties: 1
          properties:
            impressions:
              type: integer
              minimum: 0
            reactions:
              type: integer
              minimum: 0
            replies:
              type: integer
              minimum: 0
            reposts:
              type: integer
              minimum: 0
          additionalProperties: false
        unavailable:
          oneOf:
          - "$ref": "#/components/schemas/MeasurementUnavailableRecord"
          - "$ref": "#/components/schemas/NotPostizMeasurableRecord"
      additionalProperties: false
    MeasurementUnavailableRecord:
      type: object
      description: Audit set proving the retry horizon was exhausted for one measurement
        window. Carries no metrics and is excluded from every numeric rollup.
      required:
      - queue_key
      - publication_uid
      - entry_uid
      - platform
      - window
      - due_at
      - postiz_post_id
      - source
      - endpoint_class
      - first_attempted_at
      - last_attempted_at
      - attempt_count
      - attempts
      - terminal_at
      - terminal_reason
      - collector_version
      properties:
        queue_key:
          type: string
        publication_uid:
          type: string
        entry_uid:
          type: string
        platform:
          type: string
          enum:
          - x
          - linkedin
        window:
          type: string
          enum:
          - 24h
          - 72h
          - 7d
        due_at:
          type: string
          format: date-time
        postiz_post_id:
          type: string
        source:
          type: string
          enum:
          - postiz_hosted_public_api
        endpoint_class:
          type: string
          enum:
          - "/analytics/post/:id"
        first_attempted_at:
          type: string
          format: date-time
        last_attempted_at:
          type: string
          format: date-time
        attempt_count:
          type: integer
          minimum: 1
          maximum: 4
        attempts:
          type: array
          minItems: 1
          maxItems: 4
          items:
            type: object
            required:
            - attempted_at
            - result
            properties:
              attempted_at:
                type: string
                format: date-time
              result:
                type: string
                enum:
                - http_status
                - timeout
                - transport_error
                - malformed
                - empty
                - incomplete_metrics
              http_status:
                type: integer
              digest:
                type: string
                maxLength: 200
            additionalProperties: false
        terminal_at:
          type: string
          format: date-time
        terminal_reason:
          type: string
          enum:
          - retry_horizon_exhausted
        collector_version:
          type: string
      additionalProperties: false
    NotPostizMeasurableRecord:
      type: object
      description: Audit set proving a published product-lane window has no hosted
        Postiz release. Carries no metrics, no Postiz ID, and is excluded from every
        numeric rollup.
      required:
      - queue_key
      - publication_uid
      - entry_uid
      - platform
      - window
      - due_at
      - external_url
      - source
      - endpoint_class
      - catalog_from
      - catalog_to
      - map_hits
      - recorded_url_hits
      - catalog_release_url_hits
      - first_checked_at
      - last_checked_at
      - check_count
      - checks
      - terminal_at
      - terminal_reason
      - collector_version
      properties:
        queue_key:
          type: string
        publication_uid:
          type: string
        entry_uid:
          type: string
        platform:
          type: string
          enum:
          - x
          - linkedin
        window:
          type: string
          enum:
          - 24h
          - 72h
          - 7d
        due_at:
          type: string
          format: date-time
        external_url:
          type: string
        source:
          type: string
          enum:
          - hosted_postiz_catalog
        endpoint_class:
          type: string
          enum:
          - GET /posts
        catalog_from:
          type: string
          format: date-time
        catalog_to:
          type: string
          format: date-time
        map_hits:
          type: integer
          enum:
          - 0
        recorded_url_hits:
          type: integer
          enum:
          - 0
        catalog_release_url_hits:
          type: integer
          enum:
          - 0
        first_checked_at:
          type: string
          format: date-time
        last_checked_at:
          type: string
          format: date-time
        check_count:
          type: integer
          minimum: 1
          maximum: 4
        checks:
          type: array
          minItems: 1
          maxItems: 4
          items:
            type: object
            required:
            - checked_at
            - result
            properties:
              checked_at:
                type: string
                format: date-time
              result:
                type: string
                enum:
                - no_mapping
                - no_recorded_url
                - catalog_empty
              http_status:
                type: integer
              digest:
                type: string
                maxLength: 200
            additionalProperties: false
        terminal_at:
          type: string
          format: date-time
        terminal_reason:
          type: string
          enum:
          - no_hosted_postiz_release
        collector_version:
          type: string
      additionalProperties: false
    SocialQueueTask:
      type: object
      required:
      - queue_key
      - kind
      - entry_uid
      description: A Distribution-owned publication or measurement task. Kind-specific
        fields are present in addition to the common identity fields.
      properties:
        queue_key:
          type: string
        kind:
          type: string
          enum:
          - publication
          - measurement
        entry_uid:
          type: string
        package_uid:
          type: string
          nullable: true
        publication_uid:
          type: string
          nullable: true
        channel:
          type: string
          nullable: true
        platform:
          type: string
          nullable: true
          enum:
          - x
          - linkedin
        window:
          type: string
          nullable: true
          enum:
          - 24h
          - 72h
          - 7d
        scheduled_at:
          type: string
          format: date-time
          nullable: true
        due_at:
          type: string
          format: date-time
          nullable: true
        published_at:
          type: string
          format: date-time
          nullable: true
        external_url:
          type: string
          nullable: true
        approved_variant:
          type: object
          nullable: true
          additionalProperties: true
        record_url:
          type: string
          nullable: true
        capture_url:
          type: string
          nullable: true
    SocialQueueResponse:
      type: object
      required:
      - data
      - meta
      properties:
        data:
          type: array
          items:
            "$ref": "#/components/schemas/SocialQueueTask"
        meta:
          "$ref": "#/components/schemas/Meta"
    ContentItemResponse:
      type: object
      required:
      - data
      - meta
      properties:
        data:
          "$ref": "#/components/schemas/ContentItem"
        meta:
          "$ref": "#/components/schemas/Meta"
    DraftReviewState:
      type: object
      required:
      - uid
      - review_type
      - status
      - approval_level
      - claim_risk_level
      - requested_at
      properties:
        uid:
          type: string
        review_type:
          type: string
          enum:
          - draft_review
        status:
          type: string
          enum:
          - pending
          - changes_requested
        approval_level:
          type: string
          enum:
          - standard
          - strict
        claim_risk_level:
          type: string
          enum:
          - low
          - medium
          - high
          description: Claim-risk classification. On distribution approvals, medium/high
            items may be moved to changes_requested or rejected by a claim_review-scoped
            gate owner; approval and all publishing actions still require distribute
            scope.
        reviewer_feedback:
          type: string
          nullable: true
        requested_at:
          type: string
          format: date-time
        reviewed_at:
          type: string
          format: date-time
          nullable: true
        review_report:
          "$ref": "#/components/schemas/ReviewReport"
          nullable: true
      additionalProperties: false
      nullable: true
    ReviewReport:
      type: object
      description: 'Versioned review rubric output for the draft. `shadow: true` means
        the report is computed and persisted alongside the live verdict but does not
        gate anything yet — `verdict` is the decision the pipeline actually took,
        `would_be_verdict` is what the ordered gate would have decided on the evidence,
        and `reasons` names why. Once enforcement is on, `verdict` and `would_be_verdict`
        agree by construction except for live `certified_pass`, an editorial override
        that is not a would_be_verdict value. A nil dimension is one no doctrine has
        been built for yet; it renormalizes out of `review_score` rather than scoring
        zero. `coverage` states what the doctrines deliberately do NOT check, so a
        clean score never implies coverage the system does not have.'
      required:
      - rubric_version
      - review_score
      - dimensions
      - pillars
      - doctrines
      - blockers
      - verdict
      - would_be_verdict
      - reasons
      - needs_attention
      - shadow
      - coverage
      properties:
        rubric_version:
          type: string
        review_score:
          type: integer
          nullable: true
          description: 0-100 composite, nil when no dimension resolved.
        dimensions:
          type: object
          description: Per-dimension sub-scores, 0-100 or null.
          additionalProperties:
            type: integer
            nullable: true
        pillars:
          type: object
          description: Draft-grain E-E-A-T pillar scores, 0-25 points each on the
            same scale the site audit's E-E-A-T panel uses. Scored from the draft's
            own text only — never written back into the crawl-derived site signals.
            A null pillar is one this draft carried no applicable evidence for; empty
            means the doctrine did not run.
          additionalProperties:
            type: integer
            nullable: true
        doctrines:
          type: object
          description: Per-doctrine detail. Empty until the doctrines ship.
          additionalProperties: true
        blockers:
          type: array
          description: Named, inspectable reasons the draft cannot pass regardless
            of score.
          items:
            type: string
        verdict:
          type: string
          enum:
          - pass
          - revise
          - forced_pass
          - certified_pass
        would_be_verdict:
          type: string
          enum:
          - pass
          - revise
          - forced_pass
        reasons:
          type: array
          description: Ordered, named causes behind `would_be_verdict` — the blockers
            when any stand, otherwise one of `below_threshold`, `ai_verdict`, or `revision_budget_exhausted`.
            Empty on a pass.
          items:
            type: string
        needs_attention:
          type: boolean
          description: True when the draft was force-passed into the human queue on
            an exhausted revision budget rather than on merit.
        shadow:
          type: boolean
        coverage:
          type: object
          properties:
            external_web_plagiarism:
              type: string
              enum:
              - not_checked
            fabricated_measurements:
              type: string
              enum:
              - evidence_dates_only
              description: Only the date arithmetic is checked — a clean report does
                not mean the reported numbers were verified against a primary source.
          additionalProperties: true
      additionalProperties: false
      nullable: true
    ContentListResponse:
      type: object
      required:
      - data
      - meta
      properties:
        data:
          type: array
          items:
            "$ref": "#/components/schemas/ContentItem"
        meta:
          "$ref": "#/components/schemas/Meta"
    ContentBriefReview:
      type: object
      required:
      - uid
      - entry_uid
      - title
      - target_keyword
      - target_word_count
      - outline
      - status
      properties:
        uid:
          type: string
        entry_uid:
          type: string
        title:
          type: string
        target_keyword:
          type: string
          nullable: true
        target_word_count:
          type: integer
          nullable: true
        outline:
          type: string
          nullable: true
        status:
          type: string
          enum:
          - generating
          - pending_review
          - approved
          - rejected
      additionalProperties: false
    ContentDetail:
      allOf:
      - "$ref": "#/components/schemas/ContentItem"
      - type: object
        required:
        - brief
        - draft
        properties:
          brief:
            "$ref": "#/components/schemas/ContentBriefReview"
            nullable: true
          draft:
            "$ref": "#/components/schemas/ContentDraftSnapshot"
            nullable: true
    ContentDetailResponse:
      type: object
      required:
      - data
      - meta
      properties:
        data:
          "$ref": "#/components/schemas/ContentDetail"
        meta:
          "$ref": "#/components/schemas/Meta"
    ContentDraftSnapshot:
      type: object
      required:
      - uid
      - title
      - body
      - version
      - status
      - updated_at
      - review_report
      properties:
        uid:
          type: string
        title:
          type: string
        body:
          type: string
        version:
          type: integer
        status:
          type: string
          enum:
          - generating
          - draft
          - regenerating
          - in_review
          - reviewing
          - ready_for_review
          - revision_needed
          - approved
          - published
          - archived
        updated_at:
          type: string
          format: date-time
        review_report:
          "$ref": "#/components/schemas/ReviewReport"
          nullable: true
      additionalProperties: false
    ContentImportEntryInput:
      type: object
      required:
      - title
      - content_type
      properties:
        title:
          type: string
        content_type:
          type: string
          enum:
          - article
          - blog_post
          - comparison
          - listicle
          - pillar_page
          - faq_page
          - glossary_definition
          - data_study
          - tutorial
          - case_study
          - product_update
          - changelog
          - linkedin_post
          - twitter_thread
          - twitter_post
        target_keyword:
          type: string
          nullable: true
        funnel_stage:
          type: string
          nullable: true
        pillar:
          type: string
          nullable: true
        scheduled_date:
          type: string
          format: date
          nullable: true
        target_word_count:
          type: integer
          nullable: true
        priority:
          type: integer
          nullable: true
        platform:
          type: string
          nullable: true
        channel:
          type: string
          nullable: true
        topic:
          type: string
          nullable: true
        owner_notes:
          type: string
          nullable: true
        claim_safety_notes:
          type: string
          nullable: true
        metadata:
          type: object
          additionalProperties: true
    ContentImportRequest:
      type: object
      required:
      - entries
      properties:
        strategy_uid:
          type: string
          nullable: true
        replace_existing:
          type: boolean
          default: false
          description: When true, archives every existing planned or brief-generated
            entry on the strategy before importing. Omit or set false to preserve
            the calendar.
        entries:
          type: array
          items:
            "$ref": "#/components/schemas/ContentImportEntryInput"
    ContentImportResponseData:
      type: object
      required:
      - operation
      - message
      - site_slug
      - strategy_uid
      - growth_cycle_uid
      - import_batch_uid
      - imported_count
      - archived_count
      - entries
      - next_step
      properties:
        operation:
          type: string
          enum:
          - import_content
        message:
          type: string
        site_slug:
          type: string
        strategy_uid:
          type: string
        growth_cycle_uid:
          type: string
        import_batch_uid:
          type: string
        imported_count:
          type: integer
        archived_count:
          type: integer
        entries:
          type: array
          items:
            "$ref": "#/components/schemas/ContentItem"
        next_step:
          "$ref": "#/components/schemas/WorkflowNextStep"
    ContentImportResponse:
      type: object
      required:
      - data
      - meta
      properties:
        data:
          "$ref": "#/components/schemas/ContentImportResponseData"
        meta:
          "$ref": "#/components/schemas/Meta"
    PackageContentVariant:
      type: object
      required:
      - type
      - entry_uid
      - draft_uid
      - package_uid
      - requested_channel
      - channel
      - channel_approval_state
      - package_review_status
      - package_execution_state
      - cover_image
      - publication_state
      - title
      - body
      - source_draft_uid
      properties:
        type:
          type: string
          enum:
          - package_content_variant
        entry_uid:
          type: string
        draft_uid:
          type: string
        package_uid:
          type: string
        requested_channel:
          type: string
        channel:
          type: string
        channel_approval_state:
          type: string
          enum:
          - pending
          - approved
          - blocked
        package_review_status:
          type: string
        package_execution_state:
          type: string
        cover_image:
          type: object
          nullable: true
          additionalProperties: true
        publication_state:
          type: string
        publication:
          type: object
          nullable: true
          additionalProperties: true
        title:
          type: string
        body:
          type: string
        source_draft_uid:
          type: string
    PackageContentVariantResponse:
      type: object
      required:
      - data
      - meta
      properties:
        data: PackageContentVariant
        meta:
          "$ref": "#/components/schemas/Meta"
    ApprovedContentVariant:
      type: object
      required:
      - type
      - entry_uid
      - draft_uid
      - package_uid
      - requested_channel
      - channel
      - channel_approval_state
      - package_review_status
      - package_execution_state
      - cover_image
      - publication_state
      - title
      - body
      - source_draft_uid
      properties:
        type:
          type: string
          enum:
          - approved_content_variant
        entry_uid:
          type: string
        draft_uid:
          type: string
        package_uid:
          type: string
        requested_channel:
          type: string
        channel:
          type: string
        channel_approval_state:
          type: string
          enum:
          - pending
          - approved
          - blocked
        package_review_status:
          type: string
        package_execution_state:
          type: string
        cover_image:
          type: object
          nullable: true
          additionalProperties: true
        publication_state:
          type: string
        publication:
          type: object
          nullable: true
          additionalProperties: true
        title:
          type: string
        body:
          type: string
        source_draft_uid:
          type: string
    AlreadyPublishedContentVariant:
      type: object
      required:
      - type
      - entry_uid
      - draft_uid
      - package_uid
      - requested_channel
      - channel
      - channel_approval_state
      - package_review_status
      - package_execution_state
      - cover_image
      - publication_state
      - already_published
      - publication
      properties:
        type:
          type: string
          enum:
          - already_published_content_variant
        entry_uid:
          type: string
        draft_uid:
          type: string
        package_uid:
          type: string
        requested_channel:
          type: string
        channel:
          type: string
        channel_approval_state:
          type: string
          enum:
          - pending
          - approved
          - blocked
        package_review_status:
          type: string
        package_execution_state:
          type: string
        cover_image:
          type: object
          nullable: true
          additionalProperties: true
        publication_state:
          type: string
        already_published:
          type: boolean
          enum:
          - true
        publication:
          type: object
          additionalProperties: true
      additionalProperties: false
    ApprovedContentVariantResponse:
      type: object
      required:
      - data
      - meta
      properties:
        data:
          oneOf:
          - "$ref": "#/components/schemas/ApprovedContentVariant"
          - "$ref": "#/components/schemas/AlreadyPublishedContentVariant"
        meta:
          "$ref": "#/components/schemas/Meta"
    WorkflowResource:
      type: object
      properties:
        type:
          type: string
        uid:
          type: string
          nullable: true
        status:
          type: string
          nullable: true
        entry_uid:
          type: string
          nullable: true
        title:
          type: string
          nullable: true
        content_type:
          type: string
          nullable: true
        entry_status:
          type: string
          nullable: true
        growth_cycle_uid:
          type: string
          nullable: true
        brief_status:
          type: string
          nullable: true
        draft_status:
          type: string
          nullable: true
        schema_type_candidate:
          type: string
          nullable: true
          enum:
          - article
          - faq
          - how_to
        package_status:
          type: string
          nullable: true
        package_claim_risk_level:
          type: string
          nullable: true
          enum:
          - low
          - medium
          - high
        package_review_status:
          type: string
          nullable: true
        package_execution_state:
          type: string
          nullable: true
        draft_review:
          "$ref": "#/components/schemas/DraftReviewState"
        publication_state:
          type: string
          nullable: true
        publication_verified:
          type: boolean
          nullable: true
        legacy_unverified:
          type: boolean
          nullable: true
        created_at:
          type: string
          format: date-time
          nullable: true
        updated_at:
          type: string
          format: date-time
          nullable: true
        published_at:
          type: string
          format: date-time
          nullable: true
        external_url:
          type: string
          nullable: true
        triage_state:
          type: string
          nullable: true
        triage_reason:
          type: string
          nullable: true
        effective_triage_state:
          type: string
          nullable: true
        triage_needed:
          type: boolean
          nullable: true
        stale_for_triage:
          type: boolean
          nullable: true
        automation_ready:
          type: boolean
          nullable: true
        current_cycle:
          type: boolean
          nullable: true
        triage_reference_at:
          type: string
          format: date-time
          nullable: true
        archived_at:
          type: string
          format: date-time
          nullable: true
        archived_by:
          type: string
          nullable: true
        archived_reason:
          type: string
          nullable: true
        pre_archive_status:
          type: string
          nullable: true
      additionalProperties: false
    WorkflowNextStep:
      type: object
      required:
      - action
      - phase
      - message
      properties:
        action:
          type: string
        phase:
          type: string
        message:
          type: string
        endpoint:
          type: string
          nullable: true
        params:
          type: object
          additionalProperties: true
          description: Action-specific parameters. For `revision_recovery`, includes
            `revision_recovery` and `expected_version`.
      additionalProperties: false
    TriageContentRequest:
      type: object
      required:
      - triage_state
      - triage_reason
      properties:
        triage_state:
          type: string
          enum:
          - unreviewed
          - keep_publish
          - regenerate
          - repurpose
          - archive
          - delete_candidate
          description: The triage classification to apply. `archive` and `delete_candidate`
            are terminal.
        triage_reason:
          type: string
          enum:
          - stale_backlog
          - outdated_strategy
          - outdated_keyword
          - duplicate_topic
          - low_quality
          - seasonal_miss
          - owner_decision
          description: Why this triage decision was chosen.
      additionalProperties: false
    WorkflowMutationResponseData:
      type: object
      required:
      - operation
      - message
      - site_slug
      - resource
      - next_step
      properties:
        operation:
          type: string
        message:
          type: string
        site_slug:
          type: string
        job_id:
          type: string
          nullable: true
        resource:
          "$ref": "#/components/schemas/WorkflowResource"
        next_step:
          "$ref": "#/components/schemas/WorkflowNextStep"
    WorkflowMutationResponse:
      type: object
      required:
      - data
      - meta
      properties:
        data:
          "$ref": "#/components/schemas/WorkflowMutationResponseData"
        meta:
          "$ref": "#/components/schemas/Meta"
    EeatSignal:
      type: object
      required:
      - name
      - pillar
      - points_earned
      - points_possible
      - status
      - recommendation
      properties:
        name:
          type: string
        pillar:
          type: string
        points_earned:
          type: integer
        points_possible:
          type: integer
        status:
          type: string
          enum:
          - present
          - absent
        recommendation:
          type: string
          nullable: true
    EeatPillar:
      type: object
      required:
      - pillar
      - points_earned
      - points_possible
      - signals
      properties:
        pillar:
          type: string
        points_earned:
          type: integer
        points_possible:
          type: integer
        signals:
          type: array
          items:
            "$ref": "#/components/schemas/EeatSignal"
    EeatResponseData:
      type: object
      required:
      - pillars
      - total_earned
      - total_possible
      properties:
        pillars:
          type: array
          items:
            "$ref": "#/components/schemas/EeatPillar"
        total_earned:
          type: integer
        total_possible:
          type: integer
    EeatResponse:
      type: object
      required:
      - data
      - meta
      properties:
        data:
          "$ref": "#/components/schemas/EeatResponseData"
        meta:
          "$ref": "#/components/schemas/Meta"
    AuditIssue:
      type: object
      required:
      - id
      - issue_type
      - severity
      - category
      - status
      - page_url
      - description
      - persistence_days
      - detected_at
      - fixed_at
      - originated_audit_id
      properties:
        id:
          type: integer
        issue_type:
          type: string
        severity:
          type: string
        category:
          type: string
        status:
          type: string
          enum:
          - open
          - fixed
        page_url:
          type: string
          nullable: true
        description:
          type: string
          nullable: true
        persistence_days:
          type: integer
        detected_at:
          type: string
          format: date
          nullable: true
        fixed_at:
          type: string
          format: date-time
          nullable: true
        originated_audit_id:
          type: integer
          nullable: true
    SummaryIssue:
      type: object
      required:
      - issue_type
      - severity
      - category
      - page_url
      - description
      properties:
        issue_type:
          type: string
        severity:
          type: string
        category:
          type: string
        page_url:
          type: string
          nullable: true
        description:
          type: string
          nullable: true
    SummaryResponseData:
      type: object
      required:
      - scores
      - top_issues
      - keyword_movement
      - content_pipeline
      - gsc_snapshot
      properties:
        scores:
          type: object
          description: Current published TrustGrowth Score. Pillar fields are null
            when exact-date evidence is unavailable.
          required:
          - overall_score
          - score_date
          - score_age_days
          - eeat
          - content_score
          - growth_score
          - technical_score
          - performance_score
          - authority_score
          - visibility_score
          - geo_score
          properties:
            overall_score:
              type: integer
              nullable: true
            score_date:
              type: string
              format: date
              nullable: true
            score_age_days:
              type: integer
              nullable: true
              minimum: 0
            eeat:
              type: object
              required:
              - score
              - experience
              - expertise
              - authoritativeness
              - trustworthiness
              properties:
                score:
                  type: integer
                  nullable: true
                experience:
                  type: integer
                  nullable: true
                expertise:
                  type: integer
                  nullable: true
                authoritativeness:
                  type: integer
                  nullable: true
                trustworthiness:
                  type: integer
                  nullable: true
            content_score:
              type: integer
              nullable: true
            growth_score:
              type: integer
              nullable: true
            technical_score:
              type: integer
              nullable: true
            performance_score:
              type: integer
              nullable: true
            authority_score:
              type: integer
              nullable: true
            visibility_score:
              type: integer
              nullable: true
            geo_score:
              type: integer
              nullable: true
        top_issues:
          type: array
          items:
            "$ref": "#/components/schemas/SummaryIssue"
        keyword_movement:
          type: object
          required:
          - up
          - down
          - new
          properties:
            up:
              type: integer
            down:
              type: integer
            new:
              type: integer
        content_pipeline:
          type: object
          required:
          - scheduled
          - in_progress
          - published
          properties:
            scheduled:
              type: integer
            in_progress:
              type: integer
            published:
              type: integer
        gsc_snapshot:
          type: object
          required:
          - snapshot_date
          - clicks
          - impressions
          - ctr
          - position
          properties:
            snapshot_date:
              type: string
              format: date
              nullable: true
            clicks:
              type: integer
              nullable: true
            impressions:
              type: integer
              nullable: true
            ctr:
              type: number
              nullable: true
            position:
              type: number
              nullable: true
    SummaryResponse:
      type: object
      required:
      - data
      - meta
      properties:
        data:
          "$ref": "#/components/schemas/SummaryResponseData"
        meta:
          "$ref": "#/components/schemas/Meta"
    PublicationEvidencePublicProofSummary:
      type: object
      additionalProperties: false
      required:
      - available
      - overall_score
      - proof_delta
      - rank_eligible
      - score_date
      - keyword_count
      - keywords_in_top_10
      - published_content_count
      - latest_clicks
      - latest_impressions
      - latest_position
      - site_updated_on
      - source
      properties:
        available:
          type: boolean
        overall_score:
          type: integer
          nullable: true
        proof_delta:
          type: integer
          nullable: true
        rank_eligible:
          type: boolean
        score_date:
          type: string
          format: date
          nullable: true
        keyword_count:
          type: integer
        keywords_in_top_10:
          type: integer
        published_content_count:
          type: integer
        latest_clicks:
          type: integer
          nullable: true
        latest_impressions:
          type: integer
          nullable: true
        latest_position:
          type: number
          nullable: true
        site_updated_on:
          type: string
          format: date
          nullable: true
        source:
          type: object
          additionalProperties: false
          required:
          - surface
          - proof_url
          - proof_api_url
          properties:
            surface:
              type: string
              enum:
              - public_proof
            proof_url:
              type: string
            proof_api_url:
              type: string
    PublicationEvidencePacketResponseData:
      type: object
      required:
      - site
      - generated_at
      - proof_endpoints
      - public_proof_summary
      - operator_summary
      - issue_evidence
      - publication_state_summary
      - publication_safety
      properties:
        site:
          type: object
          required:
          - slug
          - name
          - domain
          properties:
            slug:
              type: string
            name:
              type: string
            domain:
              type: string
        generated_at:
          type: string
          format: date-time
        proof_endpoints:
          type: object
          additionalProperties: true
        public_proof_summary:
          "$ref": "#/components/schemas/PublicationEvidencePublicProofSummary"
        operator_summary:
          "$ref": "#/components/schemas/OperatorSummary"
        issue_evidence:
          type: object
          additionalProperties: true
        publication_state_summary:
          type: object
          additionalProperties: true
        publication_safety:
          type: object
          additionalProperties: true
    PublicationEvidencePacketResponse:
      type: object
      required:
      - data
      - meta
      properties:
        data:
          "$ref": "#/components/schemas/PublicationEvidencePacketResponseData"
        meta:
          "$ref": "#/components/schemas/Meta"
    NextAction:
      type: object
      required:
      - priority
      - business_priority_score
      - action_type
      - title
      - reason
      - recommended_owner
      - capability
      - lane
      - labels
      - rationale
      - evidence_source
      properties:
        priority:
          type: integer
        business_priority_score:
          type: integer
        action_type:
          type: string
        title:
          type: string
        reason:
          type: string
        recommended_owner:
          type: string
        capability:
          type: string
          enum:
          - api
          - ui
          - external
        lane:
          type: string
        labels:
          type: array
          items:
            type: string
        rationale:
          type: object
          additionalProperties: true
        evidence_source:
          type: object
          description: 'Polymorphic evidence for the action, discriminated by `type`
            (audit_issue, content_strategy, content_brief, content_draft, keyword_opportunity,
            site_health, visibility_prompt, visibility_sentiment). Audit-issue actions
            are AGGREGATED by issue_type: one action represents every page sharing
            that issue. `affected_count` is the true total; `pages` lists up to 10
            affected pages (highest-value surface first). `page_url`/`primary_page_url`
            is the single highest-priority page and is preserved for backward compatibility
            — do not assume one action equals one page; iterate `pages` and read `affected_count`.
            Additional per-type fields may be present.'
          additionalProperties: true
          properties:
            type:
              type: string
              description: Evidence kind / discriminator.
            issue_type:
              type: string
              nullable: true
            severity:
              type: string
              nullable: true
            category:
              type: string
              nullable: true
            page_url:
              type: string
              nullable: true
              description: Primary (highest-priority) affected page; preserved for
                back-compat.
            primary_page_url:
              type: string
              nullable: true
              description: Explicit alias of page_url for aggregated actions.
            affected_count:
              type: integer
              nullable: true
              description: Total pages affected by this issue_type (may exceed pages.length).
            pages:
              type: array
              nullable: true
              description: Affected pages, capped at 10, highest-value surface first.
              items:
                type: object
                properties:
                  page_url:
                    type: string
                  id:
                    type: integer
                  surface_tier:
                    type: string
            corroborating_pages:
              type: integer
              nullable: true
            path:
              type: string
              nullable: true
              description: In-app path for content/site actions.
    NextActionsResponseData:
      type: object
      required:
      - site_slug
      - generated_at
      - actions
      properties:
        site_slug:
          type: string
        generated_at:
          type: string
          format: date-time
        actions:
          type: array
          items:
            "$ref": "#/components/schemas/NextAction"
    NextActionsResponse:
      type: object
      required:
      - data
      - meta
      properties:
        data:
          "$ref": "#/components/schemas/NextActionsResponseData"
        meta:
          "$ref": "#/components/schemas/Meta"
    DeltaValue:
      type: object
      required:
      - before
      - after
      - delta
      properties:
        before:
          nullable: true
        after:
          nullable: true
        delta:
          nullable: true
      additionalProperties: false
    ChangeIssue:
      type: object
      required:
      - issue_type
      - severity
      - category
      - page_url
      - description
      properties:
        issue_type:
          type: string
        severity:
          type: string
        category:
          type: string
        page_url:
          type: string
          nullable: true
        description:
          type: string
          nullable: true
        first_seen_at:
          type: string
          format: date
          nullable: true
        fixed_at:
          type: string
          format: date-time
          nullable: true
    PublishedContentItem:
      type: object
      required:
      - uid
      - title
      - content_type
      - published_at
      properties:
        uid:
          type: string
        title:
          type: string
        content_type:
          type: string
          nullable: true
        published_at:
          type: string
          format: date-time
          nullable: true
    ReviewQueueReviewable:
      type: object
      required:
      - type
      - uid
      - entry_uid
      - title
      - status
      properties:
        type:
          type: string
        uid:
          type: string
          nullable: true
        entry_uid:
          type: string
          nullable: true
        title:
          type: string
          nullable: true
        status:
          type: string
          nullable: true
      additionalProperties: false
    ContentStrategyReview:
      type: object
      description: The complete persisted review content for a strategy_approval item.
        Null for every other review type.
      required:
      - uid
      - status
      - body
      - target_audience
      - content_pillars
      - themes
      - content_types
      - publishing_frequency
      - keyword_clusters
      - competitive_landscape
      - distribution_channels
      - target
      - tone_attributes
      - feedback
      - approved_at
      - created_at
      - updated_at
      properties:
        uid:
          type: string
        status:
          type: string
          enum:
          - generating
          - pending_review
          - calendar_generating
          - approved
          - archived
          - failed
        body:
          type: string
          nullable: true
        target_audience:
          type: object
          additionalProperties: true
        content_pillars:
          type: array
          items:
            type: object
            additionalProperties: true
        themes:
          type: array
          items:
            oneOf:
            - type: string
            - type: object
              additionalProperties: true
        content_types:
          type: array
          items:
            type: object
            additionalProperties: true
        publishing_frequency:
          type: object
          additionalProperties: true
        keyword_clusters:
          type: array
          items:
            type: object
            additionalProperties: true
        competitive_landscape:
          type: object
          additionalProperties: true
        distribution_channels:
          type: array
          items:
            type: string
        target:
          type: object
          additionalProperties: true
        tone_attributes:
          type: array
          items:
            type: string
        feedback:
          type: string
          nullable: true
        approved_at:
          type: string
          format: date-time
          nullable: true
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time
      additionalProperties: false
      nullable: true
    CampaignAssetApprovalContext:
      type: object
      description: 'Slice-10 task 1: present only on `campaign_asset_approval` review-queue
        items (`reviewable.type == "CampaignAssetFulfillment"`), null on every other
        item — the requirement/work-item/campaign/media-asset context a review-canvas
        card needs to render itself without a second round trip to the campaign detail
        endpoint.'
      required:
      - requirement
      - work_item
      - campaign
      - media_asset
      properties:
        requirement:
          type: object
          required:
          - uid
          - asset_type
          - stage
          properties:
            uid:
              type: string
            asset_type:
              type: string
              enum:
              - chart
              - screenshot
              - terminal_recording
              - cover
              - og_card
              - link_artifact
            stage:
              type: string
              enum:
              - submit_draft
              - package
        work_item:
          type: object
          required:
          - uid
          - title
          properties:
            uid:
              type: string
            title:
              type: string
        campaign:
          type: object
          required:
          - uid
          - key
          properties:
            uid:
              type: string
            key:
              type: string
        media_asset:
          type: object
          description: Cheaply-available MediaAsset columns only — never `resolved_url`/the
            ActiveStorage file/blob (that would reintroduce an N+1 on the review-queue
            list path).
          required:
          - uid
          - kind
          - source
          - usage_context
          - alt_text
          - source_url
          properties:
            uid:
              type: string
            kind:
              type: string
              enum:
              - image
            source:
              type: string
              enum:
              - uploaded
              - generated
              - remote_url
              - screenshot
              - chart
              - unknown
            usage_context:
              type: string
              enum:
              - blog_cover
              - social_image
            alt_text:
              type: string
              nullable: true
            source_url:
              type: string
              nullable: true
              description: Thumbnail-ish reference — populated for remote_url/screenshot/chart-sourced
                assets, null for uploaded/generated assets that only have an attached
                file.
      nullable: true
    ReviewQueueItem:
      type: object
      required:
      - uid
      - review_type
      - status
      - approval_level
      - claim_risk_level
      - requested_at
      - reviewable
      - strategy
      - campaign_human_rules
      properties:
        uid:
          type: string
        review_type:
          type: string
          enum:
          - strategy_approval
          - brief_approval
          - draft_review
          - distribution_approval
          - campaign_plan_approval
          - campaign_asset_approval
          - prompt_approval
        status:
          type: string
          enum:
          - pending
          - changes_requested
          - approved
          - rejected
          - cancelled
          - superseded
        approval_level:
          type: string
          enum:
          - standard
          - strict
        claim_risk_level:
          type: string
          enum:
          - low
          - medium
          - high
          description: Claim-risk classification. On distribution approvals, medium/high
            items may be moved to changes_requested or rejected by a claim_review-scoped
            gate owner; approval and all publishing actions still require distribute
            scope.
        claim_risk_floor:
          type: string
          enum:
          - low
          - medium
          - high
          nullable: true
          description: The calendar entry's persisted claim-risk floor for distribution
            approvals — the level a re-armed package inherits regardless of what the
            rubric proposes. Raised automatically by any package or gate that reaches
            medium/high; lowering it requires the `claim_review` scope. Null for non-package
            review types and for entries that have never carried claim risk.
        claim_evidence:
          type: string
          nullable: true
        decision:
          type: object
          nullable: true
          description: Who decided this item and when. Present once the item has been
            approved, rejected, or moved to changes_requested.
          properties:
            actor_type:
              type: string
              nullable: true
              description: user | api_key | internal_api
            actor_id:
              type: string
              nullable: true
            decided_at:
              type: string
              format: date-time
          additionalProperties: false
        inline_approvable:
          type: boolean
          description: Whether a client may approve this item without first opening
            it. Always false for `draft_review` and `distribution_approval` — anything
            that can reach the public is opened before it is approved.
        reviewer_feedback:
          type: string
          nullable: true
        requested_at:
          type: string
          format: date-time
        reviewed_at:
          type: string
          format: date-time
          nullable: true
        forced_pass:
          type: boolean
          description: The draft reached this queue because its revision budget ran
            out rather than because it satisfied the reviewer — it needs a closer
            human look than an item that passed on merit.
        reviewable:
          "$ref": "#/components/schemas/ReviewQueueReviewable"
        strategy:
          "$ref": "#/components/schemas/ContentStrategyReview"
        campaign_human_rules:
          type: array
          items:
            "$ref": "#/components/schemas/CampaignHumanRule"
          description: Applicable human-mode campaign rules at their current version.
            Empty for non-campaign content (slice 7 task 4).
        campaign_asset:
          "$ref": "#/components/schemas/CampaignAssetApprovalContext"
      additionalProperties: false
    ReviewQueueItemResponse:
      type: object
      required:
      - data
      - meta
      properties:
        data:
          "$ref": "#/components/schemas/ReviewQueueItem"
        meta:
          "$ref": "#/components/schemas/Meta"
    ReviewQueueListResponse:
      type: object
      required:
      - data
      - meta
      properties:
        data:
          type: array
          items:
            "$ref": "#/components/schemas/ReviewQueueItem"
        meta:
          "$ref": "#/components/schemas/Meta"
    ChangesResponseData:
      type: object
      required:
      - since
      - new_issues
      - fixed_issues
      - score_deltas
      - keyword_movements
      - published_content
      - gsc_metric_changes
      properties:
        since:
          type: string
        new_issues:
          type: array
          items:
            "$ref": "#/components/schemas/ChangeIssue"
        fixed_issues:
          type: array
          items:
            "$ref": "#/components/schemas/ChangeIssue"
        score_deltas:
          type: object
          required:
          - overall_score
          - eeat
          - content_score
          - growth_score
          - technical_score
          - performance_score
          - authority_score
          - visibility_score
          - geo_score
          properties:
            overall_score:
              "$ref": "#/components/schemas/DeltaValue"
            eeat:
              type: object
              properties:
                score:
                  "$ref": "#/components/schemas/DeltaValue"
                experience:
                  "$ref": "#/components/schemas/DeltaValue"
                expertise:
                  "$ref": "#/components/schemas/DeltaValue"
                authoritativeness:
                  "$ref": "#/components/schemas/DeltaValue"
                trustworthiness:
                  "$ref": "#/components/schemas/DeltaValue"
            content_score:
              "$ref": "#/components/schemas/DeltaValue"
            growth_score:
              "$ref": "#/components/schemas/DeltaValue"
            technical_score:
              "$ref": "#/components/schemas/DeltaValue"
            performance_score:
              "$ref": "#/components/schemas/DeltaValue"
            authority_score:
              "$ref": "#/components/schemas/DeltaValue"
            visibility_score:
              "$ref": "#/components/schemas/DeltaValue"
            geo_score:
              "$ref": "#/components/schemas/DeltaValue"
        keyword_movements:
          type: object
          required:
          - gained
          - lost
          - moved_up
          - moved_down
          properties:
            gained:
              type: integer
            lost:
              type: integer
            moved_up:
              type: integer
            moved_down:
              type: integer
        published_content:
          type: array
          items:
            "$ref": "#/components/schemas/PublishedContentItem"
        gsc_metric_changes:
          type: object
          properties:
            clicks:
              "$ref": "#/components/schemas/DeltaValue"
            impressions:
              "$ref": "#/components/schemas/DeltaValue"
            ctr:
              "$ref": "#/components/schemas/DeltaValue"
            position:
              "$ref": "#/components/schemas/DeltaValue"
    ChangesResponse:
      type: object
      required:
      - data
      - meta
      properties:
        data:
          "$ref": "#/components/schemas/ChangesResponseData"
        meta:
          "$ref": "#/components/schemas/Meta"
    TriggerAuditResponseData:
      type: object
      required:
      - job_id
      - site_slug
      - status
      - triggered_at
      properties:
        job_id:
          type: string
        site_slug:
          type: string
        status:
          type: string
        triggered_at:
          type: string
          format: date-time
    TriggerAuditResponse:
      type: object
      required:
      - data
      - meta
      properties:
        data:
          "$ref": "#/components/schemas/TriggerAuditResponseData"
        meta:
          "$ref": "#/components/schemas/Meta"
    AgentRunStatusSite:
      type: object
      required:
      - slug
      - name
      - domain
      properties:
        slug:
          type: string
        name:
          type: string
        domain:
          type: string
    AgentRunStatusAction:
      type: object
      required:
      - name
      - endpoint
      properties:
        name:
          type: string
        endpoint:
          type: string
    AgentRunStatusResponseData:
      type: object
      required:
      - job_id
      - run_uid
      - status
      - agent_name
      - trigger
      - triggered_at
      - started_at
      - completed_at
      - error_summary
      - site
      - action
      properties:
        job_id:
          type: string
        run_uid:
          type: string
        status:
          type: string
          enum:
          - pending
          - running
          - completed
          - failed
          - skipped
        agent_name:
          type: string
        trigger:
          type: string
        triggered_at:
          type: string
          format: date-time
        started_at:
          type: string
          format: date-time
          nullable: true
        completed_at:
          type: string
          format: date-time
          nullable: true
        error_summary:
          type: string
          nullable: true
        site:
          "$ref": "#/components/schemas/AgentRunStatusSite"
        action:
          "$ref": "#/components/schemas/AgentRunStatusAction"
        audit_cycle:
          type: object
          nullable: true
          description: For SiteAuditorAgent jobs, the audit cycle whose verdict `status`
            reports once the run is terminal. Null for every other agent, for audits
            that never opened a cycle, and for runs recorded before audit cycles existed.
          required:
          - uid
          - state
          properties:
            uid:
              type: string
            state:
              type: string
              enum:
              - running
              - complete
              - failed
    AgentRunStatusResponse:
      type: object
      required:
      - data
      - meta
      properties:
        data:
          "$ref": "#/components/schemas/AgentRunStatusResponseData"
        meta:
          "$ref": "#/components/schemas/Meta"
    McpTool:
      type: object
      required:
      - name
      - description
      - endpoint
      - inputSchema
      properties:
        name:
          type: string
        description:
          type: string
        endpoint:
          type: string
        inputSchema:
          type: object
    McpManifest:
      type: object
      required:
      - name
      - version
      - description
      - tools
      properties:
        name:
          type: string
        version:
          type: string
        description:
          type: string
        tools:
          type: array
          items:
            "$ref": "#/components/schemas/McpTool"
