openapi: 3.1.0
info:
  title: Segue Developer API
  version: "1.0.0"
  description: |
    Generate, search and download AI-generated songs from Segue.
    All songs are AI-generated assets operated by Segue. Authenticate with an API key
    issued on https://developer.segue.day (`Authorization: Bearer sgk_...`).
servers:
  - url: https://api.segue.day
  - url: https://api-dev.segue.day
    description: Development
security:
  - apiKey: []
paths:
  /v1/catalog:
    get:
      summary: List valid ids for genres, languages, usages and scenes
      responses:
        "200":
          description: Catalog
          content:
            application/json:
              schema: { $ref: "#/components/schemas/Catalog" }
  /v1/generations:
    post:
      summary: Generate a song (asynchronous)
      description: |
        Returns immediately with a generation in `queued` status. Poll `GET /v1/generations/{id}`
        or pass `callback_url` to receive a signed POST when it finishes.
        Reusing the same `idempotency_key` returns the existing generation with status 200.
      requestBody:
        required: true
        content:
          application/json:
            schema: { $ref: "#/components/schemas/GenerationRequest" }
      responses:
        "202": { description: Accepted, content: { application/json: { schema: { $ref: "#/components/schemas/Generation" } } } }
        "200": { description: Existing generation for the same idempotency_key, content: { application/json: { schema: { $ref: "#/components/schemas/Generation" } } } }
        "400": { $ref: "#/components/responses/Error" }
        "429": { $ref: "#/components/responses/Error" }
        "503": { $ref: "#/components/responses/Error" }
    get:
      summary: List recent generations
      parameters:
        - { name: limit, in: query, schema: { type: integer, minimum: 1, maximum: 100, default: 20 } }
      responses:
        "200":
          description: Generations
          content:
            application/json:
              schema:
                type: object
                properties:
                  data: { type: array, items: { $ref: "#/components/schemas/Generation" } }
  /v1/generations/{id}:
    get:
      summary: Get a generation
      description: When `status` is `succeeded`, the response includes `track` and a temporary `download` URL.
      parameters:
        - { name: id, in: path, required: true, schema: { type: string } }
      responses:
        "200": { description: Generation, content: { application/json: { schema: { $ref: "#/components/schemas/Generation" } } } }
        "404": { $ref: "#/components/responses/Error" }
  /v1/tracks/search:
    get:
      summary: Search tracks by free text and metadata
      description: |
        `q` runs a semantic search when available (`search_mode: semantic`) and otherwise falls back to
        matching titles and lyrics (`search_mode: keyword`). Comma-separate multiple values.
        Within genre / usage / scene any value may match; across parameters all must match.
        When "Include download URLs in search results" is enabled on developer.segue.day, each track has `download`.
      parameters:
        - { name: q, in: query, schema: { type: string, maxLength: 500 } }
        - { name: genre, in: query, schema: { type: string }, example: "jazz,lofi" }
        - { name: usage, in: query, schema: { type: string }, example: "wedding" }
        - { name: scene, in: query, schema: { type: string } }
        - { name: tags, in: query, schema: { type: string }, description: All of these raw tags must be present. }
        - { name: language, in: query, schema: { type: string, enum: [ja, en, zh, es] } }
        - { name: vocal, in: query, schema: { type: boolean } }
        - { name: bpm_min, in: query, schema: { type: integer } }
        - { name: bpm_max, in: query, schema: { type: integer } }
        - { name: kind, in: query, schema: { type: string, enum: [song, jingle], default: song } }
        - { name: visibility, in: query, schema: { type: string, enum: [public, private] }, description: "private returns only your private songs" }
        - { name: limit, in: query, schema: { type: integer, minimum: 1, maximum: 50, default: 20 } }
        - { name: cursor, in: query, schema: { type: string } }
      responses:
        "200":
          description: Tracks
          content:
            application/json:
              schema:
                type: object
                properties:
                  data: { type: array, items: { $ref: "#/components/schemas/Track" } }
                  search_mode: { type: string, enum: [semantic, keyword, filter] }
                  next_cursor: { type: [string, "null"] }
  /v1/tracks/{id}:
    get:
      summary: Get track metadata and lyrics
      parameters:
        - { name: id, in: path, required: true, schema: { type: string } }
      responses:
        "200": { description: Track, content: { application/json: { schema: { $ref: "#/components/schemas/Track" } } } }
        "404": { $ref: "#/components/responses/Error" }
  /v1/tracks/{id}/download-url:
    post:
      summary: Issue a temporary download URL
      parameters:
        - { name: id, in: path, required: true, schema: { type: string } }
      requestBody:
        content:
          application/json:
            schema:
              type: object
              properties:
                expires_in: { type: integer, minimum: 60, maximum: 86400, description: Seconds. Defaults to your setting. }
      responses:
        "200":
          description: Download URL
          content:
            application/json:
              schema:
                allOf:
                  - { $ref: "#/components/schemas/Download" }
                  - { type: object, properties: { track_id: { type: string } } }
        "404": { $ref: "#/components/responses/Error" }
  /v1/files/{token}:
    get:
      summary: Download audio (temporary URL)
      description: No API key is required; the URL itself is the credential. Supports `Range`.
      security: []
      parameters:
        - { name: token, in: path, required: true, schema: { type: string } }
      responses:
        "200": { description: MP3 audio, content: { audio/mpeg: {} } }
        "206": { description: Partial MP3 audio }
        "404": { $ref: "#/components/responses/Error" }
components:
  securitySchemes:
    apiKey: { type: http, scheme: bearer, description: "API key (sgk_...). X-Api-Key is also accepted." }
  responses:
    Error:
      description: Error
      headers:
        Retry-After: { schema: { type: integer } }
      content:
        application/json:
          schema:
            type: object
            properties:
              error:
                type: object
                properties:
                  code: { type: string }
                  message: { type: string }
  schemas:
    GenerationRequest:
      type: object
      additionalProperties: false
      properties:
        prompt: { type: string, maxLength: 2000, description: What the song should be about. }
        genre: { type: string, description: See /v1/catalog. Chosen automatically when omitted. }
        language: { type: string, enum: [ja, en, zh, es], default: en }
        vocal: { type: boolean, default: true }
        vocal_gender: { type: string, enum: [female, male] }
        bpm: { type: integer, minimum: 50, maximum: 200 }
        scene: { type: string, description: See /v1/catalog. }
        usage: { type: array, maxItems: 3, items: { type: string } }
        kind: { type: string, enum: [song], default: song }
        visibility: { type: string, enum: [public, private], description: "public songs are also played in the Segue app. Defaults to your setting." }
        callback_url: { type: string, format: uri, description: "https only. Requires a callback signing secret." }
        idempotency_key: { type: string, maxLength: 128, pattern: "^[A-Za-z0-9:_-]+$" }
    Generation:
      type: object
      properties:
        id: { type: string }
        object: { const: generation }
        status: { type: string, enum: [queued, running, succeeded, failed] }
        visibility: { type: string, enum: [public, private] }
        prompt: { type: [string, "null"] }
        params: { type: object }
        callback_url: { type: [string, "null"] }
        track_id: { type: [string, "null"] }
        error: { type: [object, "null"], properties: { code: { type: string }, message: { type: string } } }
        created_at: { type: string, format: date-time }
        started_at: { type: [string, "null"], format: date-time }
        finished_at: { type: [string, "null"], format: date-time }
        track: { $ref: "#/components/schemas/Track" }
        download: { $ref: "#/components/schemas/Download" }
    Track:
      type: object
      properties:
        id: { type: string }
        object: { const: track }
        ai_generated: { const: true }
        title: { type: string }
        title_reading: { type: [string, "null"] }
        kind: { type: string, enum: [song, jingle] }
        visibility: { type: string, enum: [public, private] }
        language: { type: [string, "null"] }
        duration_sec: { type: number }
        bpm: { type: [integer, "null"] }
        time_signature: { type: [string, "null"] }
        vocal: { type: boolean }
        genres: { type: array, items: { type: string } }
        usages: { type: array, items: { type: string } }
        scenes: { type: array, items: { type: string } }
        tags: { type: array, items: { type: string } }
        mood: { type: [object, "null"], description: "0..1 on energy, calm, focus, lightness, tempo" }
        lyrics: { type: [string, "null"], description: Only in GET /v1/tracks/{id} and generation results. }
        created_at: { type: string }
        download: { $ref: "#/components/schemas/Download" }
    Download:
      type: object
      properties:
        url: { type: string, format: uri }
        expires_at: { type: string, format: date-time }
    Catalog:
      type: object
      properties:
        languages: { type: array, items: { type: string } }
        genres: { type: array, items: { type: object, properties: { id: { type: string }, name: { type: string } } } }
        usages: { type: array, items: { type: object, properties: { id: { type: string }, name: { type: string }, name_ja: { type: string } } } }
        scenes: { type: array, items: { type: object, properties: { id: { type: string }, name: { type: string } } } }
        kinds: { type: array, items: { type: string } }
webhooks:
  generationFinished:
    post:
      summary: Generation finished (callback_url)
      description: |
        Sent when a generation succeeds or fails. Verify `Segue-Signature: t=<unix seconds>,v1=<hex>`
        where v1 = HMAC-SHA256(callback secret, "<t>.<raw body>"). Retries up to 5 times with backoff
        until your endpoint returns 2xx. Deduplicate with `Segue-Event-Id`.
      requestBody:
        content:
          application/json:
            schema:
              type: object
              properties:
                id: { type: string }
                type: { type: string, enum: [generation.succeeded, generation.failed] }
                created_at: { type: string, format: date-time }
                data: { type: object, properties: { generation: { $ref: "#/components/schemas/Generation" } } }
      responses:
        "200": { description: Acknowledged }
