openapi: 3.1.0
info:
  title: CreatorNode YouTube API — Hook Validator
  description: |
    Validate alignment between thumbnail, title, and video intro script.

    Detects mismatches that cause early viewer drop-off: emotion, pacing,
    content, and energy mismatches. Returns risk score and retention prediction.
  version: 1.0.0
  termsOfService: https://creatornode.io/terms
  contact:
    name: CreatorNode Support
    email: support@creatornode.io
    url: https://creatornode.io
  license:
    name: Proprietary
    url: https://creatornode.io/legal
servers:
  - url: https://api.creatornode.io/youtube
    description: Production
tags:
  - name: Hooks
    description: Video hook validation
paths:
  /v1/hook-validator:
    post:
      operationId: validateHook
      summary: Validate hook alignment
      description: Analyze alignment between thumbnail, title, and video intro script.
        Detects emotion, pacing, content, and energy mismatches that cause early
        viewer drop-off. Returns a risk score and 30-second retention
        prediction.
      tags:
        - Hooks
      security:
        - ApiKeyAuth: []
        - {}
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/ValidateHookRequest"
            examples:
              basic:
                summary: Basic validation request
                value:
                  thumbnail:
                    imageBase64: dGh1bWJuYWlsLWJhc2U2NA==
                  title: You Won't BELIEVE What Happened!
                  scriptIntro: Hey everyone, welcome back to my channel. Today we're going to talk
                    about something that happened to me last week...
          multipart/form-data:
            schema:
              $ref: "#/components/schemas/MultipartValidateHookRequest"
      responses:
        "200":
          description: Successful validation
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ValidateHookResponse"
              examples:
                high_risk:
                  summary: High risk - misalignment detected
                  value:
                    valid: false
                    validation:
                      riskLevel: high
                      riskScore: 78
                      primaryIssue: "Energy mismatch: dramatic thumbnail but calm, slow intro"
                      mismatches:
                        - type: energy
                          severity: severe
                          description: Thumbnail shows high-stakes excitement, but intro is conversational
                        - type: pacing
                          severity: moderate
                          description: Title promises immediate payoff, intro delays for 45+ seconds
                      prediction:
                        earlyDropoffPercent: 55
                        retention30s: 45
                    thumbnail:
                      emotion: excitement
                      stakes: high
                      keyElements:
                        - Shocked face
                        - Red arrow
                        - Money
                      energyLevel: high
                    title:
                      promise: Unbelievable event
                      tone: dramatic
                      clickbaitScore: 85
                    scriptHook:
                      wpm: 120
                      infoDensity: 30
                      openingEnergy: low
                      payoffDelay: 48
                      actualContent: Channel greeting and backstory
                      deliversPromise: false
                    recommendations:
                      - type: warning
                        title: Energy mismatch detected
                        message: Your thumbnail promises high energy, but your intro is too slow.
                          Consider starting with the key moment.
                        priority: high
                    metadata:
                      requestId: req_hook_123
                      processingTimeMs: 950
                      aiUsed: true
                low_risk:
                  summary: Low risk - good alignment
                  value:
                    valid: true
                    validation:
                      riskLevel: low
                      riskScore: 15
                      primaryIssue: null
                      mismatches: []
                      prediction:
                        earlyDropoffPercent: 11
                        retention30s: 89
                    recommendations:
                      - type: strength
                        title: Strong hook-thumbnail alignment
                        message: Your intro delivers on the thumbnail promise quickly.
                        priority: low
                    metadata:
                      requestId: req_hook_456
                      processingTimeMs: 820
                      aiUsed: true
        "400":
          $ref: "#/components/responses/ValidationError"
        "401":
          $ref: "#/components/responses/AuthError"
        "429":
          $ref: "#/components/responses/RateLimited"
        "500":
          $ref: "#/components/responses/ServerError"
components:
  securitySchemes:
    ApiKeyAuth:
      type: apiKey
      in: header
      name: X-API-Key
      description: APIM subscription key for authenticated access. Without a key,
        requests use free tier limits.
  responses:
    ValidationError:
      description: Validation error
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/ErrorResponse"
          example:
            success: false
            error:
              code: VALIDATION_ERROR
              message: Request validation failed
              details:
                issues:
                  - path:
                      - variants
                    message: Required
    AuthError:
      description: Authentication error
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/ErrorResponse"
          example:
            success: false
            error:
              code: API_KEY_INVALID
              message: Invalid API key
    RateLimited:
      description: Rate limit exceeded
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/ErrorResponse"
          example:
            success: false
            error:
              code: RATE_LIMITED
              message: Rate limit exceeded
              details:
                retryAfter: 60
    ServerError:
      description: Internal server error
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/ErrorResponse"
          example:
            success: false
            error:
              code: INTERNAL_ERROR
              message: An internal error occurred
  schemas:
    ValidateHookRequest:
      type: object
      properties:
        thumbnail:
          type: object
          description: Thumbnail image (base64 payload, max 2MB)
          properties:
            imageBase64:
              type: string
              description: Base64-encoded thumbnail image
          required:
            - imageBase64
        title:
          type: string
          minLength: 1
          maxLength: 200
          description: Video title
        scriptIntro:
          type: string
          minLength: 10
          maxLength: 1500
          description: First 60-90 seconds of video script
        locale:
          type: string
          maxLength: 10
          default: en
          description: Locale for text analysis
        options:
          $ref: "#/components/schemas/ValidateHookOptions"
      required:
        - thumbnail
        - title
        - scriptIntro
    ValidateHookOptions:
      type: object
      properties:
        maxAnalysisSeconds:
          type: integer
          minimum: 10
          maximum: 120
          default: 30
          description: Seconds of intro to analyze. Default is 30 (the typical hook
            window). Increase for longer-form intros.
        scriptDurationSeconds:
          type: integer
          minimum: 5
          maximum: 600
          description: How many seconds of speech the submitted scriptIntro represents.
            Required for WPM calculation. Omit if unknown.
    MultipartValidateHookRequest:
      type: object
      properties:
        metadata:
          type: string
          description: JSON metadata (title, scriptIntro, options)
        thumbnail:
          type: string
          format: binary
          description: Thumbnail image file (max 2MB)
      required:
        - thumbnail
        - metadata
    ValidateHookResponse:
      type: object
      properties:
        valid:
          type: boolean
          description: Whether content passed validation (true = low/medium risk)
        validation:
          $ref: "#/components/schemas/CrossValidation"
        thumbnail:
          $ref: "#/components/schemas/ThumbnailAnalysis"
        title:
          $ref: "#/components/schemas/TitleAnalysis"
        scriptHook:
          $ref: "#/components/schemas/ScriptHookAnalysis"
        recommendations:
          type: array
          items:
            $ref: "#/components/schemas/Recommendation"
        metadata:
          $ref: "#/components/schemas/ResponseMeta"
    CrossValidation:
      type: object
      properties:
        riskLevel:
          type: string
          enum:
            - low
            - medium
            - high
            - critical
        riskScore:
          type: number
          minimum: 0
          maximum: 100
          description: 0-100, higher = more risk of drop-off
        primaryIssue:
          type:
            - string
            - "null"
        mismatches:
          type: array
          items:
            $ref: "#/components/schemas/Mismatch"
        prediction:
          type: object
          properties:
            earlyDropoffPercent:
              type: number
              minimum: 0
              maximum: 100
              description: Predicted % viewers who leave in first 30s
            retention30s:
              type: number
              minimum: 0
              maximum: 100
              description: Predicted % viewers retained past the hook (= 100 -
                earlyDropoffPercent)
    Mismatch:
      type: object
      properties:
        type:
          type: string
          enum:
            - emotion
            - pacing
            - content
            - energy
        severity:
          type: string
          enum:
            - minor
            - moderate
            - severe
        description:
          type: string
    ThumbnailAnalysis:
      type: object
      properties:
        emotion:
          type: string
        stakes:
          type: string
          enum:
            - low
            - medium
            - high
        keyElements:
          type: array
          items:
            type: string
        energyLevel:
          type: string
          enum:
            - low
            - medium
            - high
        clickbaitScore:
          type: number
          minimum: 0
          maximum: 100
          description: Visual clickbait score (0-100)
        description:
          type: string
    TitleAnalysis:
      type: object
      properties:
        promise:
          type: string
        tone:
          type: string
          enum:
            - neutral
            - urgent
            - dramatic
            - mysterious
            - exciting
        clickbaitScore:
          type: number
          minimum: 0
          maximum: 100
    ScriptHookAnalysis:
      type: object
      properties:
        wpm:
          type:
            - number
            - "null"
          description: Words per minute (null if scriptDurationSeconds was not provided)
        infoDensity:
          type: number
          minimum: 0
          maximum: 100
          description: Information density (0-100)
        openingEnergy:
          type: string
          enum:
            - low
            - medium
            - high
        payoffDelay:
          type:
            - number
            - "null"
          description: Seconds until promise is addressed
        actualContent:
          type: string
          description: Summary of what intro actually covers
        deliversPromise:
          type: boolean
    Recommendation:
      type: object
      properties:
        type:
          type: string
          enum:
            - upgrade
            - top_up
            - feature
            - tip
            - warning
            - fix
            - strength
        title:
          type: string
        message:
          type: string
        action:
          type: object
          properties:
            label:
              type: string
            url:
              type: string
              format: uri
        priority:
          type: string
          enum:
            - low
            - medium
            - high
    ResponseMeta:
      type: object
      properties:
        requestId:
          type: string
        processingTimeMs:
          type: integer
        variantCount:
          type: integer
        aiUsed:
          type: boolean
        cached:
          type: boolean
    ErrorResponse:
      type: object
      properties:
        success:
          type: boolean
          const: false
        error:
          $ref: "#/components/schemas/ErrorDetail"
        meta:
          $ref: "#/components/schemas/ResponseMeta"
        recommendations:
          type: array
          items:
            $ref: "#/components/schemas/Recommendation"
    ErrorDetail:
      type: object
      properties:
        code:
          type: string
          description: Machine-readable error code
        message:
          type: string
          description: Human-readable message
        details:
          type: object
          additionalProperties: true
    ValidateHookDemoRequest:
      type: object
      properties:
        thumbnailImageBase64:
          type: string
        title:
          type: string
          minLength: 1
          maxLength: 200
        scriptIntro:
          type: string
          minLength: 1
          maxLength: 10000
      required:
        - thumbnailImageBase64
        - title
        - scriptIntro
    ValidateHookDemoResponse:
      type: object
      properties:
        validation:
          type: object
          properties:
            riskLevel:
              type: string
            thumbnailAnalysis:
              type: object
              properties:
                mainSubject:
                  type: string
                emotion:
                  type: string
                textOverlay:
                  type:
                    - string
                    - "null"
                objects:
                  type: array
                  items:
                    type: string
                colorScheme:
                  type: array
                  items:
                    type: string
                attentionGrabbing:
                  type: integer
            titleAnalysis:
              type: object
              properties:
                hook:
                  type: string
                emotion:
                  type: string
                promiseType:
                  type: string
                powerWords:
                  type: array
                  items:
                    type: string
                clarity:
                  type: integer
            scriptHookAnalysis:
              type: object
              properties:
                openingHook:
                  type: string
                hookType:
                  type: string
                emotionalTrigger:
                  type: string
                clarity:
                  type: integer
                engagement:
                  type: integer
            crossValidation:
              type: object
              properties:
                thumbnailTitleAlignment:
                  type: integer
                titleScriptAlignment:
                  type: integer
                overallCoherence:
                  type: integer
                mismatches:
                  type: array
                  items:
                    type: object
                    properties:
                      type:
                        type: string
                      severity:
                        type: string
                      description:
                        type: string
                      suggestion:
                        type: string
        recommendations:
          type: array
          items:
            type: object
            properties:
              type:
                type: string
                enum:
                  - improvement
                  - warning
              priority:
                type: string
                enum:
                  - high
                  - medium
                  - low
              message:
                type: string
              impact:
                type: string
        meta:
          type: object
          properties:
            requestId:
              type: string
            processingTimeMs:
              type: integer
            demoMode:
              type: boolean
              enum:
                - true
            warning:
              type: string
      required:
        - validation
        - meta
