openapi: 3.1.0
info:
  title: Kairos API
  version: "1.0"
  description: |
    Send Kairos a long-form video; get back short, edited, publish-ready clips.

    Kairos runs the full production pipeline for you: moment selection
    (BRAID), cutting, silence removal, animated captions, background music
    with automatic ducking, and per-clip editorial metadata. One request in,
    one webhook out.

    **Base URL:** `https://api.kairoslabs.pro/v1`

    **Plans:** API access requires the Business plan. Processing is billed
    against your plan's monthly minutes.
  contact:
    name: Kairos
    url: https://kairoslabs.pro
servers:
  - url: https://api.kairoslabs.pro/v1
    description: Production
security:
  - ApiKeyHeader: []
  - ApiKeyBearer: []
tags:
  - name: Projects
    description: A project is one source video and the clips produced from it.

paths:
  /projects:
    post:
      tags: [Projects]
      operationId: createProject
      summary: Create a project
      description: |
        Submit a video URL. Kairos ingests, transcribes, selects every
        quality moment and produces clips — the count adapts to the
        content (a one-hour show typically yields 5–15). With `autoEdit`
        (default **true**) the edit agent then polishes EVERY produced
        clip: tightens the hook, cuts dead air, styles captions and places
        your music.

        Processing is asynchronous — poll `GET /projects/{projectId}` or
        provide `webhookUrl`.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/CreateProjectRequest"
            examples:
              agency:
                summary: Fully automated with brand music
                value:
                  videoUrl: "https://youtube.com/watch?v=dQw4w9WgXcQ"
                  autoEdit: true
                  styleGoal: viral
                  musicTrackId: "your-uploaded-track-id"
                  clipDuration: { minSec: 45, maxSec: 90 }
                  webhookUrl: "https://your-app.com/hooks/kairos"
      responses:
        "201":
          description: Project accepted and queued.
          content:
            application/json:
              schema:
                type: object
                properties:
                  success: { type: boolean }
                  data:
                    type: object
                    properties:
                      projectId: { type: string, example: "6a494b7ff2fb3d847befe559" }
                      status: { type: string, example: queued }
                      autoEdit: { type: boolean }
        "400": { $ref: "#/components/responses/BadRequest" }
        "401": { $ref: "#/components/responses/Unauthorized" }
        "402":
          description: Plan limit reached or Business plan required.

  /projects/{projectId}:
    get:
      tags: [Projects]
      operationId: getProject
      summary: Get project status and clips
      description: |
        `status` is the clipping pipeline state. `editing` is true while the
        edit agent is still polishing clips (autoEdit projects only) —
        delivery is complete when `status` is `completed` and `editing` is
        false.
      parameters:
        - $ref: "#/components/parameters/ProjectId"
      responses:
        "200":
          description: Project state.
          content:
            application/json:
              schema:
                type: object
                properties:
                  success: { type: boolean }
                  data: { $ref: "#/components/schemas/Project" }
        "401": { $ref: "#/components/responses/Unauthorized" }
        "404": { $ref: "#/components/responses/NotFound" }

  /projects/{projectId}/clips/{index}/download:
    get:
      tags: [Projects]
      operationId: downloadClip
      summary: Get a rendered clip download URL
      description: |
        Renders the clip (or serves the cached render) with every edit
        burned in — cuts, transitions, animated captions, music with
        ducking — and returns a signed URL valid for one hour.

        The first render of a clip can take up to a few minutes for long
        clips; subsequent calls are instant.
      parameters:
        - $ref: "#/components/parameters/ProjectId"
        - name: index
          in: path
          required: true
          schema: { type: integer, minimum: 0 }
          description: Clip index within the project.
      responses:
        "200":
          description: Signed download URL.
          content:
            application/json:
              schema:
                type: object
                properties:
                  success: { type: boolean }
                  data:
                    type: object
                    properties:
                      url:
                        type: string
                        example: "https://storage.googleapis.com/…"
        "401": { $ref: "#/components/responses/Unauthorized" }
        "404": { $ref: "#/components/responses/NotFound" }

webhooks:
  job.completed:
    post:
      summary: Clipping finished
      description: |
        Fired when moment selection and clip extraction complete. For
        autoEdit projects the clips are not final yet — wait for
        `project.edited`.
      requestBody:
        content:
          application/json:
            schema:
              type: object
              properties:
                event: { type: string, const: job.completed }
                jobId: { type: string }
                status: { type: string }
                timestamp: { type: string, format: date-time }
      responses:
        "200": { description: Acknowledge with any 2xx. }

  project.edited:
    post:
      summary: Edited clips delivered
      description: |
        Fired once the edit agent has finished every clip. This is the
        "clips delivered" signal — download URLs are ready to fetch.
      requestBody:
        content:
          application/json:
            schema:
              type: object
              properties:
                event: { type: string, const: project.edited }
                projectId: { type: string }
                timestamp: { type: string, format: date-time }
                clips:
                  type: array
                  items: { $ref: "#/components/schemas/ClipSummary" }
      responses:
        "200": { description: Acknowledge with any 2xx. }

components:
  securitySchemes:
    ApiKeyHeader:
      type: apiKey
      in: header
      name: x-api-key
      description: Create keys at platform.kairoslabs.pro → Dashboard → API.
    ApiKeyBearer:
      type: http
      scheme: bearer
      description: The same key may be sent as a Bearer token.

  parameters:
    ProjectId:
      name: projectId
      in: path
      required: true
      schema: { type: string }

  responses:
    BadRequest:
      description: Validation failed — the error message lists field-level issues.
    Unauthorized:
      description: Missing, invalid or revoked API key.
    NotFound:
      description: Project or clip not found (or owned by another account).

  schemas:
    CreateProjectRequest:
      type: object
      required: [videoUrl]
      properties:
        videoUrl:
          type: string
          format: uri
          description: Public video URL (YouTube, Twitch VOD, direct file…).
        language:
          type: string
          enum: [auto, en, tr]
          default: auto
        autoEdit:
          type: boolean
          default: true
          description: Run the edit agent on every clip after clipping.
        styleGoal:
          type: string
          maxLength: 200
          description: >
            Editorial direction for the agent, e.g. "viral",
            "professional — clean corporate look".
        musicTrackId:
          type: string
          pattern: "^[\\w-]+$"
          description: >
            Your music track, applied to every clip (upload it in the
            dashboard editor first). When omitted, clips are delivered with
            NO music — the edit agent never substitutes its own choice.
        clipDuration:
          type: object
          description: Clip duration bounds in seconds (default min 60).
          properties:
            minSec: { type: integer, minimum: 10, maximum: 300 }
            maxSec: { type: integer, minimum: 15, maximum: 600 }
        webhookUrl:
          type: string
          format: uri
          description: Receives `job.completed` and `project.edited` events.

    Project:
      type: object
      properties:
        projectId: { type: string }
        status:
          type: string
          enum: [queued, processing, completed, failed]
        editing:
          type: boolean
          description: True while the edit agent is still working on clips.
        error: { type: [string, "null"] }
        clips:
          type: array
          items: { $ref: "#/components/schemas/ClipSummary" }

    ClipSummary:
      type: object
      properties:
        index: { type: integer }
        title: { type: string, example: "Bu yatırım modeli her şeyi değiştiriyor" }
        durationSec: { type: number, example: 42.7 }
        viralityScore: { type: [number, "null"], example: 87 }
        hook:
          type: [string, "null"]
          description: Why the first seconds grab — written by the edit agent.
        reasoning:
          type: [string, "null"]
          description: Why this moment was selected and edited this way.
        edit:
          type: [object, "null"]
          properties:
            status: { type: string, enum: [queued, running, done, failed] }
            summary:
              type: [string, "null"]
              example: "sirius captions · 3 segments (tightened) · music chill-1"
        download:
          type: string
          description: Relative API path returning a signed download URL.
