openapi: 3.1.0
info:
  title: Gibeon API
  version: 0.1.0
  description: Public REST API for the Gibeon digital signage platform.
    Authenticate with a Supabase OAuth 2.1 access token (Authorization Code +
    PKCE). See https://www.gibeon.io/auth.md.
servers:
  - url: https://www.gibeon.io
    description: Production
components:
  securitySchemes:
    oauth2:
      type: oauth2
      flows:
        authorizationCode:
          authorizationUrl: https://cuztuwusnyvfuictgesw.supabase.co/auth/v1/oauth/authorize
          tokenUrl: https://cuztuwusnyvfuictgesw.supabase.co/auth/v1/oauth/token
          scopes:
            gibeon:api: Full tenant-scoped access to the Gibeon API
  schemas:
    HealthResponse:
      type:
        - object
      properties:
        ok:
          type:
            - boolean
          enum:
            - true
        request_id:
          type:
            - string
      required:
        - ok
        - request_id
      title: HealthResponse
    ErrorResponse:
      type:
        - object
      properties:
        error:
          type:
            - object
          properties:
            code:
              type:
                - string
              enum:
                - unauthorized
                - forbidden
                - not_found
                - conflict
                - validation_failed
                - rate_limited
                - internal_error
            message:
              type:
                - string
            request_id:
              type:
                - string
          required:
            - code
            - message
            - request_id
      required:
        - error
      title: ErrorResponse
    Player:
      type:
        - object
      properties:
        id:
          type:
            - string
          format: uuid
        name:
          type:
            - string
        location:
          type:
            - string
            - "null"
        status:
          type:
            - string
          enum:
            - online
            - offline
        last_seen:
          type:
            - string
            - "null"
        playlist_id:
          type:
            - string
            - "null"
          format: uuid
        sequence_id:
          type:
            - string
            - "null"
          format: uuid
        planning_id:
          type:
            - string
            - "null"
          format: uuid
        group_id:
          type:
            - string
            - "null"
          format: uuid
        volume:
          type:
            - integer
        is_active:
          type:
            - boolean
        created_at:
          type:
            - string
        updated_at:
          type:
            - string
      required:
        - id
        - name
        - location
        - status
        - last_seen
        - playlist_id
        - sequence_id
        - planning_id
        - group_id
        - volume
        - is_active
        - created_at
        - updated_at
      title: Player
    PatchPlayerBody:
      type:
        - object
      properties:
        name:
          type:
            - string
          minLength: 1
        location:
          type:
            - string
            - "null"
        playlist_id:
          type:
            - string
            - "null"
          format: uuid
        sequence_id:
          type:
            - string
            - "null"
          format: uuid
        planning_id:
          type:
            - string
            - "null"
          format: uuid
        group_id:
          type:
            - string
            - "null"
          format: uuid
        volume:
          type:
            - integer
          minimum: 0
          maximum: 100
        is_active:
          type:
            - boolean
      title: PatchPlayerBody
    PlayerListResponse:
      type:
        - object
      properties:
        data:
          type:
            - array
          items:
            type:
              - object
            properties:
              id:
                type:
                  - string
                format: uuid
              name:
                type:
                  - string
              location:
                type:
                  - string
                  - "null"
              status:
                type:
                  - string
                enum:
                  - online
                  - offline
              last_seen:
                type:
                  - string
                  - "null"
              playlist_id:
                type:
                  - string
                  - "null"
                format: uuid
              sequence_id:
                type:
                  - string
                  - "null"
                format: uuid
              planning_id:
                type:
                  - string
                  - "null"
                format: uuid
              group_id:
                type:
                  - string
                  - "null"
                format: uuid
              volume:
                type:
                  - integer
              is_active:
                type:
                  - boolean
              created_at:
                type:
                  - string
              updated_at:
                type:
                  - string
            required:
              - id
              - name
              - location
              - status
              - last_seen
              - playlist_id
              - sequence_id
              - planning_id
              - group_id
              - volume
              - is_active
              - created_at
              - updated_at
            title: Player
        next_cursor:
          type:
            - string
            - "null"
      required:
        - data
        - next_cursor
      title: PlayerListResponse
    PlayerSingleResponse:
      type:
        - object
      properties:
        data:
          type:
            - object
          properties:
            id:
              type:
                - string
              format: uuid
            name:
              type:
                - string
            location:
              type:
                - string
                - "null"
            status:
              type:
                - string
              enum:
                - online
                - offline
            last_seen:
              type:
                - string
                - "null"
            playlist_id:
              type:
                - string
                - "null"
              format: uuid
            sequence_id:
              type:
                - string
                - "null"
              format: uuid
            planning_id:
              type:
                - string
                - "null"
              format: uuid
            group_id:
              type:
                - string
                - "null"
              format: uuid
            volume:
              type:
                - integer
            is_active:
              type:
                - boolean
            created_at:
              type:
                - string
            updated_at:
              type:
                - string
          required:
            - id
            - name
            - location
            - status
            - last_seen
            - playlist_id
            - sequence_id
            - planning_id
            - group_id
            - volume
            - is_active
            - created_at
            - updated_at
          title: Player
      required:
        - data
      title: PlayerSingleResponse
    PatchPlayerResponse:
      type:
        - object
      properties:
        data:
          type:
            - object
          properties:
            id:
              type:
                - string
              format: uuid
            name:
              type:
                - string
            location:
              type:
                - string
                - "null"
            status:
              type:
                - string
              enum:
                - online
                - offline
            last_seen:
              type:
                - string
                - "null"
            playlist_id:
              type:
                - string
                - "null"
              format: uuid
            sequence_id:
              type:
                - string
                - "null"
              format: uuid
            planning_id:
              type:
                - string
                - "null"
              format: uuid
            group_id:
              type:
                - string
                - "null"
              format: uuid
            volume:
              type:
                - integer
            is_active:
              type:
                - boolean
            created_at:
              type:
                - string
            updated_at:
              type:
                - string
          required:
            - id
            - name
            - location
            - status
            - last_seen
            - playlist_id
            - sequence_id
            - planning_id
            - group_id
            - volume
            - is_active
            - created_at
            - updated_at
          title: Player
        publish_required:
          type:
            - boolean
      required:
        - data
        - publish_required
      title: PatchPlayerResponse
    PublishOneResponse:
      type:
        - object
      properties:
        data:
          type:
            - object
          properties:
            player_id:
              type:
                - string
              format: uuid
            snapshot_built_at:
              type:
                - string
            snapshot_size_bytes:
              type:
                - integer
          required:
            - player_id
            - snapshot_built_at
            - snapshot_size_bytes
      required:
        - data
      title: PublishOneResponse
    PublishAllResponse:
      type:
        - object
      properties:
        data:
          type:
            - object
          properties:
            published:
              type:
                - integer
            failed:
              type:
                - integer
            players:
              type:
                - array
              items:
                discriminator:
                  propertyName: status
                oneOf:
                  - type:
                      - object
                    properties:
                      id:
                        type:
                          - string
                        format: uuid
                      status:
                        type:
                          - string
                        enum:
                          - published
                      snapshot_built_at:
                        type:
                          - string
                    required:
                      - id
                      - status
                      - snapshot_built_at
                  - type:
                      - object
                    properties:
                      id:
                        type:
                          - string
                        format: uuid
                      status:
                        type:
                          - string
                        enum:
                          - skipped
                      reason:
                        type:
                          - string
                    required:
                      - id
                      - status
                      - reason
                  - type:
                      - object
                    properties:
                      id:
                        type:
                          - string
                        format: uuid
                      status:
                        type:
                          - string
                        enum:
                          - failed
                      reason:
                        type:
                          - string
                    required:
                      - id
                      - status
                      - reason
          required:
            - published
            - failed
            - players
      required:
        - data
      title: PublishAllResponse
    Playlist:
      type:
        - object
      properties:
        id:
          type:
            - string
          format: uuid
        name:
          type:
            - string
        default_interval:
          type:
            - integer
        image_uri:
          type:
            - string
            - "null"
        created_at:
          type:
            - string
        updated_at:
          type:
            - string
      required:
        - id
        - name
        - default_interval
        - image_uri
        - created_at
        - updated_at
      title: Playlist
    CreatePlaylistBody:
      type:
        - object
      properties:
        name:
          type:
            - string
          minLength: 1
        default_interval:
          type:
            - integer
          minimum: 1
        image_uri:
          type:
            - string
            - "null"
      required:
        - name
      title: CreatePlaylistBody
    PatchPlaylistBody:
      type:
        - object
      properties:
        name:
          type:
            - string
          minLength: 1
        default_interval:
          type:
            - integer
          minimum: 1
        image_uri:
          type:
            - string
            - "null"
      title: PatchPlaylistBody
    PlaylistListResponse:
      type:
        - object
      properties:
        data:
          type:
            - array
          items:
            type:
              - object
            properties:
              id:
                type:
                  - string
                format: uuid
              name:
                type:
                  - string
              default_interval:
                type:
                  - integer
              image_uri:
                type:
                  - string
                  - "null"
              created_at:
                type:
                  - string
              updated_at:
                type:
                  - string
            required:
              - id
              - name
              - default_interval
              - image_uri
              - created_at
              - updated_at
            title: Playlist
        next_cursor:
          type:
            - string
            - "null"
      required:
        - data
        - next_cursor
      title: PlaylistListResponse
    PlaylistSingleResponse:
      type:
        - object
      properties:
        data:
          type:
            - object
          properties:
            id:
              type:
                - string
              format: uuid
            name:
              type:
                - string
            default_interval:
              type:
                - integer
            image_uri:
              type:
                - string
                - "null"
            created_at:
              type:
                - string
            updated_at:
              type:
                - string
          required:
            - id
            - name
            - default_interval
            - image_uri
            - created_at
            - updated_at
          title: Playlist
      required:
        - data
      title: PlaylistSingleResponse
    PlaylistWithItemsResponse:
      type:
        - object
      properties:
        data:
          type:
            - object
          properties:
            id:
              type:
                - string
              format: uuid
            name:
              type:
                - string
            default_interval:
              type:
                - integer
            image_uri:
              type:
                - string
                - "null"
            created_at:
              type:
                - string
            updated_at:
              type:
                - string
            items:
              type:
                - array
              items:
                type:
                  - object
                properties:
                  id:
                    type:
                      - string
                    format: uuid
                  playlist_id:
                    type:
                      - string
                    format: uuid
                  type:
                    type:
                      - string
                  name:
                    type:
                      - string
                      - "null"
                  asset_id:
                    type:
                      - string
                      - "null"
                    format: uuid
                  external_url:
                    type:
                      - string
                      - "null"
                  duration_seconds:
                    type:
                      - integer
                  sort_order:
                    type:
                      - integer
                  is_selected:
                    type:
                      - boolean
                  schedule_start:
                    type:
                      - string
                      - "null"
                  schedule_end:
                    type:
                      - string
                      - "null"
                  config:
                    type:
                      - object
                    additionalProperties: {}
                  created_at:
                    type:
                      - string
                required:
                  - id
                  - playlist_id
                  - type
                  - name
                  - asset_id
                  - external_url
                  - duration_seconds
                  - sort_order
                  - is_selected
                  - schedule_start
                  - schedule_end
                  - config
                  - created_at
                title: ContentItem
          required:
            - id
            - name
            - default_interval
            - image_uri
            - created_at
            - updated_at
            - items
      required:
        - data
      title: PlaylistWithItemsResponse
    ContentItem:
      type:
        - object
      properties:
        id:
          type:
            - string
          format: uuid
        playlist_id:
          type:
            - string
          format: uuid
        type:
          type:
            - string
        name:
          type:
            - string
            - "null"
        asset_id:
          type:
            - string
            - "null"
          format: uuid
        external_url:
          type:
            - string
            - "null"
        duration_seconds:
          type:
            - integer
        sort_order:
          type:
            - integer
        is_selected:
          type:
            - boolean
        schedule_start:
          type:
            - string
            - "null"
        schedule_end:
          type:
            - string
            - "null"
        config:
          type:
            - object
          additionalProperties: {}
        created_at:
          type:
            - string
      required:
        - id
        - playlist_id
        - type
        - name
        - asset_id
        - external_url
        - duration_seconds
        - sort_order
        - is_selected
        - schedule_start
        - schedule_end
        - config
        - created_at
      title: ContentItem
    CreateContentItemBody:
      type:
        - object
      properties:
        type:
          type:
            - string
          enum:
            - image
            - video
            - image_slideshow
            - youtube
        name:
          type:
            - string
            - "null"
        asset_id:
          type:
            - string
          format: uuid
        external_url:
          type:
            - string
          format: uri
        config:
          type:
            - object
          additionalProperties: {}
        duration_seconds:
          type:
            - integer
          minimum: 0
        is_selected:
          type:
            - boolean
        schedule_start:
          type:
            - string
            - "null"
        schedule_end:
          type:
            - string
            - "null"
      required:
        - type
      title: CreateContentItemBody
    PatchContentItemBody:
      type:
        - object
      properties:
        name:
          type:
            - string
            - "null"
        duration_seconds:
          type:
            - integer
          minimum: 0
        is_selected:
          type:
            - boolean
        schedule_start:
          type:
            - string
            - "null"
        schedule_end:
          type:
            - string
            - "null"
        external_url:
          type:
            - string
          format: uri
        asset_id:
          type:
            - string
          format: uuid
        config:
          type:
            - object
          additionalProperties: {}
      title: PatchContentItemBody
    ReorderItemsBody:
      type:
        - object
      properties:
        item_ids:
          type:
            - array
          items:
            type:
              - string
            format: uuid
          minItems: 1
      required:
        - item_ids
      title: ReorderItemsBody
    ContentItemListResponse:
      type:
        - object
      properties:
        data:
          type:
            - array
          items:
            type:
              - object
            properties:
              id:
                type:
                  - string
                format: uuid
              playlist_id:
                type:
                  - string
                format: uuid
              type:
                type:
                  - string
              name:
                type:
                  - string
                  - "null"
              asset_id:
                type:
                  - string
                  - "null"
                format: uuid
              external_url:
                type:
                  - string
                  - "null"
              duration_seconds:
                type:
                  - integer
              sort_order:
                type:
                  - integer
              is_selected:
                type:
                  - boolean
              schedule_start:
                type:
                  - string
                  - "null"
              schedule_end:
                type:
                  - string
                  - "null"
              config:
                type:
                  - object
                additionalProperties: {}
              created_at:
                type:
                  - string
            required:
              - id
              - playlist_id
              - type
              - name
              - asset_id
              - external_url
              - duration_seconds
              - sort_order
              - is_selected
              - schedule_start
              - schedule_end
              - config
              - created_at
            title: ContentItem
        next_cursor:
          type:
            - string
            - "null"
      required:
        - data
        - next_cursor
      title: ContentItemListResponse
    ContentItemSingleResponse:
      type:
        - object
      properties:
        data:
          type:
            - object
          properties:
            id:
              type:
                - string
              format: uuid
            playlist_id:
              type:
                - string
              format: uuid
            type:
              type:
                - string
            name:
              type:
                - string
                - "null"
            asset_id:
              type:
                - string
                - "null"
              format: uuid
            external_url:
              type:
                - string
                - "null"
            duration_seconds:
              type:
                - integer
            sort_order:
              type:
                - integer
            is_selected:
              type:
                - boolean
            schedule_start:
              type:
                - string
                - "null"
            schedule_end:
              type:
                - string
                - "null"
            config:
              type:
                - object
              additionalProperties: {}
            created_at:
              type:
                - string
          required:
            - id
            - playlist_id
            - type
            - name
            - asset_id
            - external_url
            - duration_seconds
            - sort_order
            - is_selected
            - schedule_start
            - schedule_end
            - config
            - created_at
          title: ContentItem
      required:
        - data
      title: ContentItemSingleResponse
    Sequence:
      type:
        - object
      properties:
        id:
          type:
            - string
          format: uuid
        name:
          type:
            - string
        created_at:
          type:
            - string
        updated_at:
          type:
            - string
      required:
        - id
        - name
        - created_at
        - updated_at
      title: Sequence
    CreateSequenceBody:
      type:
        - object
      properties:
        name:
          type:
            - string
          minLength: 1
      required:
        - name
      title: CreateSequenceBody
    PatchSequenceBody:
      type:
        - object
      properties:
        name:
          type:
            - string
          minLength: 1
      title: PatchSequenceBody
    SequenceItem:
      type:
        - object
      properties:
        id:
          type:
            - string
          format: uuid
        sequence_id:
          type:
            - string
          format: uuid
        playlist_id:
          type:
            - string
          format: uuid
        sort_order:
          type:
            - integer
        play_mode:
          type:
            - string
          enum:
            - full
            - repeat
            - duration
        repeat_count:
          type:
            - integer
        max_duration_seconds:
          type:
            - integer
            - "null"
        created_at:
          type:
            - string
      required:
        - id
        - sequence_id
        - playlist_id
        - sort_order
        - play_mode
        - repeat_count
        - max_duration_seconds
        - created_at
      title: SequenceItem
    CreateSequenceItemBody:
      type:
        - object
      properties:
        playlist_id:
          type:
            - string
          format: uuid
        play_mode:
          type:
            - string
          enum:
            - full
            - repeat
            - duration
        repeat_count:
          type:
            - integer
          minimum: 1
        max_duration_seconds:
          type:
            - integer
            - "null"
          minimum: 1
      required:
        - playlist_id
      title: CreateSequenceItemBody
    PatchSequenceItemBody:
      type:
        - object
      properties:
        playlist_id:
          type:
            - string
          format: uuid
        play_mode:
          type:
            - string
          enum:
            - full
            - repeat
            - duration
        repeat_count:
          type:
            - integer
          minimum: 1
        max_duration_seconds:
          type:
            - integer
            - "null"
          minimum: 1
      title: PatchSequenceItemBody
    SequenceListResponse:
      type:
        - object
      properties:
        data:
          type:
            - array
          items:
            type:
              - object
            properties:
              id:
                type:
                  - string
                format: uuid
              name:
                type:
                  - string
              created_at:
                type:
                  - string
              updated_at:
                type:
                  - string
            required:
              - id
              - name
              - created_at
              - updated_at
            title: Sequence
        next_cursor:
          type:
            - string
            - "null"
      required:
        - data
        - next_cursor
      title: SequenceListResponse
    SequenceSingleResponse:
      type:
        - object
      properties:
        data:
          type:
            - object
          properties:
            id:
              type:
                - string
              format: uuid
            name:
              type:
                - string
            created_at:
              type:
                - string
            updated_at:
              type:
                - string
          required:
            - id
            - name
            - created_at
            - updated_at
          title: Sequence
      required:
        - data
      title: SequenceSingleResponse
    SequenceWithItemsResponse:
      type:
        - object
      properties:
        data:
          type:
            - object
          properties:
            id:
              type:
                - string
              format: uuid
            name:
              type:
                - string
            created_at:
              type:
                - string
            updated_at:
              type:
                - string
            items:
              type:
                - array
              items:
                type:
                  - object
                properties:
                  id:
                    type:
                      - string
                    format: uuid
                  sequence_id:
                    type:
                      - string
                    format: uuid
                  playlist_id:
                    type:
                      - string
                    format: uuid
                  sort_order:
                    type:
                      - integer
                  play_mode:
                    type:
                      - string
                    enum:
                      - full
                      - repeat
                      - duration
                  repeat_count:
                    type:
                      - integer
                  max_duration_seconds:
                    type:
                      - integer
                      - "null"
                  created_at:
                    type:
                      - string
                required:
                  - id
                  - sequence_id
                  - playlist_id
                  - sort_order
                  - play_mode
                  - repeat_count
                  - max_duration_seconds
                  - created_at
                title: SequenceItem
          required:
            - id
            - name
            - created_at
            - updated_at
            - items
      required:
        - data
      title: SequenceWithItemsResponse
    SequenceItemListResponse:
      type:
        - object
      properties:
        data:
          type:
            - array
          items:
            type:
              - object
            properties:
              id:
                type:
                  - string
                format: uuid
              sequence_id:
                type:
                  - string
                format: uuid
              playlist_id:
                type:
                  - string
                format: uuid
              sort_order:
                type:
                  - integer
              play_mode:
                type:
                  - string
                enum:
                  - full
                  - repeat
                  - duration
              repeat_count:
                type:
                  - integer
              max_duration_seconds:
                type:
                  - integer
                  - "null"
              created_at:
                type:
                  - string
            required:
              - id
              - sequence_id
              - playlist_id
              - sort_order
              - play_mode
              - repeat_count
              - max_duration_seconds
              - created_at
            title: SequenceItem
        next_cursor:
          type:
            - string
            - "null"
      required:
        - data
        - next_cursor
      title: SequenceItemListResponse
    SequenceItemSingleResponse:
      type:
        - object
      properties:
        data:
          type:
            - object
          properties:
            id:
              type:
                - string
              format: uuid
            sequence_id:
              type:
                - string
              format: uuid
            playlist_id:
              type:
                - string
              format: uuid
            sort_order:
              type:
                - integer
            play_mode:
              type:
                - string
              enum:
                - full
                - repeat
                - duration
            repeat_count:
              type:
                - integer
            max_duration_seconds:
              type:
                - integer
                - "null"
            created_at:
              type:
                - string
          required:
            - id
            - sequence_id
            - playlist_id
            - sort_order
            - play_mode
            - repeat_count
            - max_duration_seconds
            - created_at
          title: SequenceItem
      required:
        - data
      title: SequenceItemSingleResponse
    Planning:
      type:
        - object
      properties:
        id:
          type:
            - string
          format: uuid
        name:
          type:
            - string
        created_at:
          type:
            - string
        updated_at:
          type:
            - string
      required:
        - id
        - name
        - created_at
        - updated_at
      title: Planning
    CreatePlanningBody:
      type:
        - object
      properties:
        name:
          type:
            - string
          minLength: 1
      required:
        - name
      title: CreatePlanningBody
    PatchPlanningBody:
      type:
        - object
      properties:
        name:
          type:
            - string
          minLength: 1
      title: PatchPlanningBody
    PlanningItem:
      type:
        - object
      properties:
        id:
          type:
            - string
          format: uuid
        planning_id:
          type:
            - string
          format: uuid
        item_type:
          type:
            - string
          enum:
            - playlist
            - sequence
            - action
        playlist_id:
          type:
            - string
            - "null"
          format: uuid
        sequence_id:
          type:
            - string
            - "null"
          format: uuid
        date:
          type:
            - string
            - "null"
        start_time_minutes:
          type:
            - integer
        duration_minutes:
          type:
            - integer
        priority:
          type:
            - integer
        is_recurring:
          type:
            - boolean
        recurrence_days:
          type:
            - array
          items:
            type:
              - string
            enum:
              - sun
              - mon
              - tue
              - wed
              - thu
              - fri
              - sat
        recurrence_start:
          type:
            - string
            - "null"
        recurrence_end:
          type:
            - string
            - "null"
        created_at:
          type:
            - string
      required:
        - id
        - planning_id
        - item_type
        - playlist_id
        - sequence_id
        - date
        - start_time_minutes
        - duration_minutes
        - priority
        - is_recurring
        - recurrence_days
        - recurrence_start
        - recurrence_end
        - created_at
      title: PlanningItem
    CreatePlanningItemBody:
      type:
        - object
      properties:
        item_type:
          type:
            - string
          enum:
            - playlist
            - sequence
        playlist_id:
          type:
            - string
            - "null"
          format: uuid
        sequence_id:
          type:
            - string
            - "null"
          format: uuid
        date:
          type:
            - string
            - "null"
        start_time_minutes:
          type:
            - integer
          minimum: 0
          maximum: 1439
        duration_minutes:
          type:
            - integer
          minimum: 1
        priority:
          type:
            - integer
          minimum: 0
          maximum: 100
        is_recurring:
          type:
            - boolean
        recurrence_days:
          type:
            - array
          items:
            type:
              - string
            enum:
              - sun
              - mon
              - tue
              - wed
              - thu
              - fri
              - sat
        recurrence_start:
          type:
            - string
            - "null"
        recurrence_end:
          type:
            - string
            - "null"
      required:
        - item_type
        - start_time_minutes
        - duration_minutes
        - is_recurring
      title: CreatePlanningItemBody
    PatchPlanningItemBody:
      type:
        - object
      properties:
        playlist_id:
          type:
            - string
            - "null"
          format: uuid
        sequence_id:
          type:
            - string
            - "null"
          format: uuid
        date:
          type:
            - string
            - "null"
        start_time_minutes:
          type:
            - integer
          minimum: 0
          maximum: 1439
        duration_minutes:
          type:
            - integer
          minimum: 1
        priority:
          type:
            - integer
          minimum: 0
          maximum: 100
        is_recurring:
          type:
            - boolean
        recurrence_days:
          type:
            - array
          items:
            type:
              - string
            enum:
              - sun
              - mon
              - tue
              - wed
              - thu
              - fri
              - sat
        recurrence_start:
          type:
            - string
            - "null"
        recurrence_end:
          type:
            - string
            - "null"
      title: PatchPlanningItemBody
    PlanningListResponse:
      type:
        - object
      properties:
        data:
          type:
            - array
          items:
            type:
              - object
            properties:
              id:
                type:
                  - string
                format: uuid
              name:
                type:
                  - string
              created_at:
                type:
                  - string
              updated_at:
                type:
                  - string
            required:
              - id
              - name
              - created_at
              - updated_at
            title: Planning
        next_cursor:
          type:
            - string
            - "null"
      required:
        - data
        - next_cursor
      title: PlanningListResponse
    PlanningSingleResponse:
      type:
        - object
      properties:
        data:
          type:
            - object
          properties:
            id:
              type:
                - string
              format: uuid
            name:
              type:
                - string
            created_at:
              type:
                - string
            updated_at:
              type:
                - string
          required:
            - id
            - name
            - created_at
            - updated_at
          title: Planning
      required:
        - data
      title: PlanningSingleResponse
    PlanningWithItemsResponse:
      type:
        - object
      properties:
        data:
          type:
            - object
          properties:
            id:
              type:
                - string
              format: uuid
            name:
              type:
                - string
            created_at:
              type:
                - string
            updated_at:
              type:
                - string
            items:
              type:
                - array
              items:
                type:
                  - object
                properties:
                  id:
                    type:
                      - string
                    format: uuid
                  planning_id:
                    type:
                      - string
                    format: uuid
                  item_type:
                    type:
                      - string
                    enum:
                      - playlist
                      - sequence
                      - action
                  playlist_id:
                    type:
                      - string
                      - "null"
                    format: uuid
                  sequence_id:
                    type:
                      - string
                      - "null"
                    format: uuid
                  date:
                    type:
                      - string
                      - "null"
                  start_time_minutes:
                    type:
                      - integer
                  duration_minutes:
                    type:
                      - integer
                  priority:
                    type:
                      - integer
                  is_recurring:
                    type:
                      - boolean
                  recurrence_days:
                    type:
                      - array
                    items:
                      type:
                        - string
                      enum:
                        - sun
                        - mon
                        - tue
                        - wed
                        - thu
                        - fri
                        - sat
                  recurrence_start:
                    type:
                      - string
                      - "null"
                  recurrence_end:
                    type:
                      - string
                      - "null"
                  created_at:
                    type:
                      - string
                required:
                  - id
                  - planning_id
                  - item_type
                  - playlist_id
                  - sequence_id
                  - date
                  - start_time_minutes
                  - duration_minutes
                  - priority
                  - is_recurring
                  - recurrence_days
                  - recurrence_start
                  - recurrence_end
                  - created_at
                title: PlanningItem
          required:
            - id
            - name
            - created_at
            - updated_at
            - items
      required:
        - data
      title: PlanningWithItemsResponse
    PlanningItemListResponse:
      type:
        - object
      properties:
        data:
          type:
            - array
          items:
            type:
              - object
            properties:
              id:
                type:
                  - string
                format: uuid
              planning_id:
                type:
                  - string
                format: uuid
              item_type:
                type:
                  - string
                enum:
                  - playlist
                  - sequence
                  - action
              playlist_id:
                type:
                  - string
                  - "null"
                format: uuid
              sequence_id:
                type:
                  - string
                  - "null"
                format: uuid
              date:
                type:
                  - string
                  - "null"
              start_time_minutes:
                type:
                  - integer
              duration_minutes:
                type:
                  - integer
              priority:
                type:
                  - integer
              is_recurring:
                type:
                  - boolean
              recurrence_days:
                type:
                  - array
                items:
                  type:
                    - string
                  enum:
                    - sun
                    - mon
                    - tue
                    - wed
                    - thu
                    - fri
                    - sat
              recurrence_start:
                type:
                  - string
                  - "null"
              recurrence_end:
                type:
                  - string
                  - "null"
              created_at:
                type:
                  - string
            required:
              - id
              - planning_id
              - item_type
              - playlist_id
              - sequence_id
              - date
              - start_time_minutes
              - duration_minutes
              - priority
              - is_recurring
              - recurrence_days
              - recurrence_start
              - recurrence_end
              - created_at
            title: PlanningItem
        next_cursor:
          type:
            - string
            - "null"
      required:
        - data
        - next_cursor
      title: PlanningItemListResponse
    PlanningItemSingleResponse:
      type:
        - object
      properties:
        data:
          type:
            - object
          properties:
            id:
              type:
                - string
              format: uuid
            planning_id:
              type:
                - string
              format: uuid
            item_type:
              type:
                - string
              enum:
                - playlist
                - sequence
                - action
            playlist_id:
              type:
                - string
                - "null"
              format: uuid
            sequence_id:
              type:
                - string
                - "null"
              format: uuid
            date:
              type:
                - string
                - "null"
            start_time_minutes:
              type:
                - integer
            duration_minutes:
              type:
                - integer
            priority:
              type:
                - integer
            is_recurring:
              type:
                - boolean
            recurrence_days:
              type:
                - array
              items:
                type:
                  - string
                enum:
                  - sun
                  - mon
                  - tue
                  - wed
                  - thu
                  - fri
                  - sat
            recurrence_start:
              type:
                - string
                - "null"
            recurrence_end:
              type:
                - string
                - "null"
            created_at:
              type:
                - string
          required:
            - id
            - planning_id
            - item_type
            - playlist_id
            - sequence_id
            - date
            - start_time_minutes
            - duration_minutes
            - priority
            - is_recurring
            - recurrence_days
            - recurrence_start
            - recurrence_end
            - created_at
          title: PlanningItem
      required:
        - data
      title: PlanningItemSingleResponse
    Asset:
      type:
        - object
      properties:
        id:
          type:
            - string
          format: uuid
        name:
          type:
            - string
        asset_type:
          type:
            - string
        mime_type:
          type:
            - string
            - "null"
        file_size:
          type:
            - integer
            - "null"
        width:
          type:
            - integer
            - "null"
        height:
          type:
            - integer
            - "null"
        duration:
          type:
            - number
            - "null"
        folder_id:
          type:
            - string
            - "null"
          format: uuid
        url:
          type:
            - string
            - "null"
        status:
          type:
            - string
          enum:
            - pending
            - uploading
            - processing
            - ready
            - failed
        created_at:
          type:
            - string
        updated_at:
          type:
            - string
      required:
        - id
        - name
        - asset_type
        - mime_type
        - file_size
        - width
        - height
        - duration
        - folder_id
        - url
        - status
        - created_at
        - updated_at
      title: Asset
    CreateAssetBody:
      type:
        - object
      properties:
        name:
          type:
            - string
          minLength: 1
        asset_type:
          type:
            - string
          enum:
            - image
            - pdf
            - html
        mime_type:
          type:
            - string
          minLength: 1
        file_size:
          type:
            - integer
          minimum: 1
        folder_id:
          type:
            - string
            - "null"
          format: uuid
      required:
        - name
        - asset_type
        - mime_type
        - file_size
      title: CreateAssetBody
    PatchAssetBody:
      type:
        - object
      properties:
        name:
          type:
            - string
          minLength: 1
        folder_id:
          type:
            - string
            - "null"
          format: uuid
      title: PatchAssetBody
    AssetUploadInstruction:
      type:
        - object
      properties:
        url:
          type:
            - string
        method:
          type:
            - string
          enum:
            - PUT
        expires_at:
          type:
            - string
      required:
        - url
        - method
        - expires_at
      title: AssetUploadInstruction
    CreateAssetResponse:
      type:
        - object
      properties:
        data:
          type:
            - object
          properties:
            id:
              type:
                - string
              format: uuid
            name:
              type:
                - string
            asset_type:
              type:
                - string
            mime_type:
              type:
                - string
                - "null"
            file_size:
              type:
                - integer
                - "null"
            width:
              type:
                - integer
                - "null"
            height:
              type:
                - integer
                - "null"
            duration:
              type:
                - number
                - "null"
            folder_id:
              type:
                - string
                - "null"
              format: uuid
            url:
              type:
                - string
                - "null"
            status:
              type:
                - string
              enum:
                - pending
                - uploading
                - processing
                - ready
                - failed
            created_at:
              type:
                - string
            updated_at:
              type:
                - string
            upload:
              type:
                - object
              properties:
                url:
                  type:
                    - string
                method:
                  type:
                    - string
                  enum:
                    - PUT
                expires_at:
                  type:
                    - string
              required:
                - url
                - method
                - expires_at
              title: AssetUploadInstruction
          required:
            - id
            - name
            - asset_type
            - mime_type
            - file_size
            - width
            - height
            - duration
            - folder_id
            - url
            - status
            - created_at
            - updated_at
            - upload
      required:
        - data
      title: CreateAssetResponse
    AssetListResponse:
      type:
        - object
      properties:
        data:
          type:
            - array
          items:
            type:
              - object
            properties:
              id:
                type:
                  - string
                format: uuid
              name:
                type:
                  - string
              asset_type:
                type:
                  - string
              mime_type:
                type:
                  - string
                  - "null"
              file_size:
                type:
                  - integer
                  - "null"
              width:
                type:
                  - integer
                  - "null"
              height:
                type:
                  - integer
                  - "null"
              duration:
                type:
                  - number
                  - "null"
              folder_id:
                type:
                  - string
                  - "null"
                format: uuid
              url:
                type:
                  - string
                  - "null"
              status:
                type:
                  - string
                enum:
                  - pending
                  - uploading
                  - processing
                  - ready
                  - failed
              created_at:
                type:
                  - string
              updated_at:
                type:
                  - string
            required:
              - id
              - name
              - asset_type
              - mime_type
              - file_size
              - width
              - height
              - duration
              - folder_id
              - url
              - status
              - created_at
              - updated_at
            title: Asset
        next_cursor:
          type:
            - string
            - "null"
      required:
        - data
        - next_cursor
      title: AssetListResponse
    AssetSingleResponse:
      type:
        - object
      properties:
        data:
          type:
            - object
          properties:
            id:
              type:
                - string
              format: uuid
            name:
              type:
                - string
            asset_type:
              type:
                - string
            mime_type:
              type:
                - string
                - "null"
            file_size:
              type:
                - integer
                - "null"
            width:
              type:
                - integer
                - "null"
            height:
              type:
                - integer
                - "null"
            duration:
              type:
                - number
                - "null"
            folder_id:
              type:
                - string
                - "null"
              format: uuid
            url:
              type:
                - string
                - "null"
            status:
              type:
                - string
              enum:
                - pending
                - uploading
                - processing
                - ready
                - failed
            created_at:
              type:
                - string
            updated_at:
              type:
                - string
          required:
            - id
            - name
            - asset_type
            - mime_type
            - file_size
            - width
            - height
            - duration
            - folder_id
            - url
            - status
            - created_at
            - updated_at
          title: Asset
      required:
        - data
      title: AssetSingleResponse
    Folder:
      type:
        - object
      properties:
        id:
          type:
            - string
          format: uuid
        name:
          type:
            - string
        parent_id:
          type:
            - string
            - "null"
          format: uuid
        created_at:
          type:
            - string
        updated_at:
          type:
            - string
      required:
        - id
        - name
        - parent_id
        - created_at
        - updated_at
      title: Folder
    CreateFolderBody:
      type:
        - object
      properties:
        name:
          type:
            - string
          minLength: 1
        parent_id:
          type:
            - string
            - "null"
          format: uuid
      required:
        - name
      title: CreateFolderBody
    PatchFolderBody:
      type:
        - object
      properties:
        name:
          type:
            - string
          minLength: 1
        parent_id:
          type:
            - string
            - "null"
          format: uuid
      title: PatchFolderBody
    FolderListResponse:
      type:
        - object
      properties:
        data:
          type:
            - array
          items:
            type:
              - object
            properties:
              id:
                type:
                  - string
                format: uuid
              name:
                type:
                  - string
              parent_id:
                type:
                  - string
                  - "null"
                format: uuid
              created_at:
                type:
                  - string
              updated_at:
                type:
                  - string
            required:
              - id
              - name
              - parent_id
              - created_at
              - updated_at
            title: Folder
        next_cursor:
          type:
            - string
            - "null"
      required:
        - data
        - next_cursor
      title: FolderListResponse
    FolderSingleResponse:
      type:
        - object
      properties:
        data:
          type:
            - object
          properties:
            id:
              type:
                - string
              format: uuid
            name:
              type:
                - string
            parent_id:
              type:
                - string
                - "null"
              format: uuid
            created_at:
              type:
                - string
            updated_at:
              type:
                - string
          required:
            - id
            - name
            - parent_id
            - created_at
            - updated_at
          title: Folder
      required:
        - data
      title: FolderSingleResponse
  responses:
    Unauthorized:
      description: Missing, invalid, or revoked API key
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/ErrorResponse"
    RateLimited:
      description: Too many requests — back off and retry
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/ErrorResponse"
      headers:
        Retry-After:
          schema:
            type: integer
          description: Seconds until the window resets
    NotFound:
      description: Resource not found
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/ErrorResponse"
    ValidationFailed:
      description: Request body or query failed schema validation
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/ErrorResponse"
    Conflict:
      description: Resource cannot be deleted or modified — referenced elsewhere
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/ErrorResponse"
security:
  - oauth2:
      - gibeon:api
paths:
  /v1/health:
    get:
      summary: Health check
      description: Returns ok=true when the API is reachable. No auth required.
      security: []
      responses:
        "200":
          description: API is healthy
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HealthResponse"
  /v1/players:
    get:
      summary: List players
      description: Returns all players in the calling tenant. Optionally filter by
        `status` (online|offline) or `group_id`.
      parameters:
        - name: status
          in: query
          required: false
          schema:
            type: string
            enum:
              - online
              - offline
        - name: group_id
          in: query
          required: false
          schema:
            type: string
            format: uuid
        - name: limit
          in: query
          required: false
          schema:
            type: integer
            default: 25
      responses:
        "200":
          description: List of players
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/PlayerListResponse"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "429":
          $ref: "#/components/responses/RateLimited"
  /v1/players/{id}:
    parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
          format: uuid
    get:
      summary: Get player
      responses:
        "200":
          description: Player
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/PlayerSingleResponse"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "404":
          $ref: "#/components/responses/NotFound"
    patch:
      summary: Update player
      description: "Update mutable player fields. Response includes `publish_required:
        true` when one of `playlist_id`, `sequence_id`, `planning_id`,
        `group_id` was changed — the caller must POST `/publish` before the
        player picks up the change."
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/PatchPlayerBody"
      responses:
        "200":
          description: Updated player
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/PatchPlayerResponse"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "404":
          $ref: "#/components/responses/NotFound"
        "422":
          $ref: "#/components/responses/ValidationFailed"
  /v1/players/{id}/publish:
    parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
          format: uuid
    post:
      summary: Publish a single player
      description: Builds and stores a new content snapshot for the player. Returns
        when the snapshot is written.
      responses:
        "200":
          description: Snapshot built
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/PublishOneResponse"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "404":
          $ref: "#/components/responses/NotFound"
  /v1/players/publish:
    post:
      summary: Publish every player in the tenant
      description: Iterates every player in the tenant and builds a snapshot for each.
        Players with no playlist/sequence/planning assigned are skipped with
        reason `no_content_assigned`. Per-player failures do not abort the
        batch.
      responses:
        "200":
          description: Batch result
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/PublishAllResponse"
        "401":
          $ref: "#/components/responses/Unauthorized"
  /v1/playlists:
    get:
      summary: List playlists
      responses:
        "200":
          description: Playlist list
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/PlaylistListResponse"
        "401":
          $ref: "#/components/responses/Unauthorized"
    post:
      summary: Create playlist
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/CreatePlaylistBody"
      responses:
        "201":
          description: Created
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/PlaylistSingleResponse"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "422":
          $ref: "#/components/responses/ValidationFailed"
  /v1/playlists/{id}:
    parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
          format: uuid
    get:
      summary: Get playlist
      description: With `?include=items` returns the playlist with its content items inline.
      parameters:
        - name: include
          in: query
          required: false
          schema:
            type: string
            enum:
              - items
      responses:
        "200":
          description: Playlist (with optional items)
          content:
            application/json:
              schema:
                oneOf:
                  - $ref: "#/components/schemas/PlaylistSingleResponse"
                  - $ref: "#/components/schemas/PlaylistWithItemsResponse"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "404":
          $ref: "#/components/responses/NotFound"
    patch:
      summary: Update playlist
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/PatchPlaylistBody"
      responses:
        "200":
          description: Updated
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/PlaylistSingleResponse"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "404":
          $ref: "#/components/responses/NotFound"
        "422":
          $ref: "#/components/responses/ValidationFailed"
    delete:
      summary: Delete playlist
      responses:
        "204":
          description: Deleted
        "401":
          $ref: "#/components/responses/Unauthorized"
        "404":
          $ref: "#/components/responses/NotFound"
  /v1/playlists/{id}/items:
    parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
          format: uuid
    get:
      summary: List content items
      responses:
        "200":
          description: Items
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ContentItemListResponse"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "404":
          $ref: "#/components/responses/NotFound"
    post:
      summary: Append content item
      description: Body shape depends on `type`. Sort_order is auto-assigned to max+1.
        Only `image`, `video`, `image_slideshow`, `youtube` are accepted;
        deprecated types return 422 `content_type_deprecated`.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/CreateContentItemBody"
      responses:
        "201":
          description: Created
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ContentItemSingleResponse"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "404":
          $ref: "#/components/responses/NotFound"
        "422":
          $ref: "#/components/responses/ValidationFailed"
  /v1/playlists/{id}/items/reorder:
    parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
          format: uuid
    put:
      summary: Reorder content items
      description: "Atomic: must list every item_id belonging to the playlist exactly
        once. Missing or extra ids return 422."
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/ReorderItemsBody"
      responses:
        "204":
          description: Reordered
        "401":
          $ref: "#/components/responses/Unauthorized"
        "404":
          $ref: "#/components/responses/NotFound"
        "422":
          $ref: "#/components/responses/ValidationFailed"
  /v1/playlists/{id}/items/{item_id}:
    parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
          format: uuid
      - name: item_id
        in: path
        required: true
        schema:
          type: string
          format: uuid
    patch:
      summary: Update content item
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/PatchContentItemBody"
      responses:
        "200":
          description: Updated
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ContentItemSingleResponse"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "404":
          $ref: "#/components/responses/NotFound"
        "422":
          $ref: "#/components/responses/ValidationFailed"
    delete:
      summary: Delete content item
      responses:
        "204":
          description: Deleted
        "401":
          $ref: "#/components/responses/Unauthorized"
        "404":
          $ref: "#/components/responses/NotFound"
  /v1/sequences:
    get:
      summary: List sequences
      responses:
        "200":
          description: Sequences
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/SequenceListResponse"
        "401":
          $ref: "#/components/responses/Unauthorized"
    post:
      summary: Create sequence
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/CreateSequenceBody"
      responses:
        "201":
          description: Created
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/SequenceSingleResponse"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "422":
          $ref: "#/components/responses/ValidationFailed"
  /v1/sequences/{id}:
    parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
          format: uuid
    get:
      summary: Get sequence
      parameters:
        - name: include
          in: query
          required: false
          schema:
            type: string
            enum:
              - items
      responses:
        "200":
          description: Sequence (with optional items)
          content:
            application/json:
              schema:
                oneOf:
                  - $ref: "#/components/schemas/SequenceSingleResponse"
                  - $ref: "#/components/schemas/SequenceWithItemsResponse"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "404":
          $ref: "#/components/responses/NotFound"
    patch:
      summary: Update sequence
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/PatchSequenceBody"
      responses:
        "200":
          description: Updated
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/SequenceSingleResponse"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "404":
          $ref: "#/components/responses/NotFound"
        "422":
          $ref: "#/components/responses/ValidationFailed"
    delete:
      summary: Delete sequence
      responses:
        "204":
          description: Deleted
        "401":
          $ref: "#/components/responses/Unauthorized"
        "404":
          $ref: "#/components/responses/NotFound"
  /v1/sequences/{id}/items:
    parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
          format: uuid
    get:
      summary: List sequence items
      responses:
        "200":
          description: Items
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/SequenceItemListResponse"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "404":
          $ref: "#/components/responses/NotFound"
    post:
      summary: Append sequence item
      description: play_mode='repeat' requires repeat_count >= 1; play_mode='duration'
        requires max_duration_seconds > 0.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/CreateSequenceItemBody"
      responses:
        "201":
          description: Created
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/SequenceItemSingleResponse"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "404":
          $ref: "#/components/responses/NotFound"
        "422":
          $ref: "#/components/responses/ValidationFailed"
  /v1/sequences/{id}/items/reorder:
    parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
          format: uuid
    put:
      summary: Reorder sequence items
      description: Atomic two-phase update to honour UNIQUE(sequence_id, sort_order).
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/ReorderItemsBody"
      responses:
        "204":
          description: Reordered
        "401":
          $ref: "#/components/responses/Unauthorized"
        "404":
          $ref: "#/components/responses/NotFound"
        "422":
          $ref: "#/components/responses/ValidationFailed"
  /v1/sequences/{id}/items/{item_id}:
    parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
          format: uuid
      - name: item_id
        in: path
        required: true
        schema:
          type: string
          format: uuid
    patch:
      summary: Update sequence item
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/PatchSequenceItemBody"
      responses:
        "200":
          description: Updated
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/SequenceItemSingleResponse"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "404":
          $ref: "#/components/responses/NotFound"
        "422":
          $ref: "#/components/responses/ValidationFailed"
    delete:
      summary: Delete sequence item
      responses:
        "204":
          description: Deleted
        "401":
          $ref: "#/components/responses/Unauthorized"
        "404":
          $ref: "#/components/responses/NotFound"
  /v1/planning:
    get:
      summary: List planning
      responses:
        "200":
          description: List
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/PlanningListResponse"
        "401":
          $ref: "#/components/responses/Unauthorized"
    post:
      summary: Create planning
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/CreatePlanningBody"
      responses:
        "201":
          description: Created
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/PlanningSingleResponse"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "422":
          $ref: "#/components/responses/ValidationFailed"
  /v1/planning/{id}:
    parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
          format: uuid
    get:
      summary: Get planning
      parameters:
        - name: include
          in: query
          required: false
          schema:
            type: string
            enum:
              - items
      responses:
        "200":
          description: Planning (with optional items)
          content:
            application/json:
              schema:
                oneOf:
                  - $ref: "#/components/schemas/PlanningSingleResponse"
                  - $ref: "#/components/schemas/PlanningWithItemsResponse"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "404":
          $ref: "#/components/responses/NotFound"
    patch:
      summary: Update planning
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/PatchPlanningBody"
      responses:
        "200":
          description: Updated
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/PlanningSingleResponse"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "404":
          $ref: "#/components/responses/NotFound"
        "422":
          $ref: "#/components/responses/ValidationFailed"
    delete:
      summary: Delete planning
      responses:
        "204":
          description: Deleted
        "401":
          $ref: "#/components/responses/Unauthorized"
        "404":
          $ref: "#/components/responses/NotFound"
  /v1/planning/{id}/items:
    parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
          format: uuid
    get:
      summary: List planning items
      responses:
        "200":
          description: Items
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/PlanningItemListResponse"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "404":
          $ref: "#/components/responses/NotFound"
    post:
      summary: Create planning item
      description: item_type='playlist' requires playlist_id; 'sequence' requires
        sequence_id. is_recurring=true requires recurrence_days >= 1 day;
        is_recurring=false requires date. item_type='action' is not supported in
        MVP (422 item_type_deprecated).
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/CreatePlanningItemBody"
      responses:
        "201":
          description: Created
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/PlanningItemSingleResponse"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "404":
          $ref: "#/components/responses/NotFound"
        "422":
          $ref: "#/components/responses/ValidationFailed"
  /v1/planning/{id}/items/{item_id}:
    parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
          format: uuid
      - name: item_id
        in: path
        required: true
        schema:
          type: string
          format: uuid
    patch:
      summary: Update planning item
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/PatchPlanningItemBody"
      responses:
        "200":
          description: Updated
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/PlanningItemSingleResponse"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "404":
          $ref: "#/components/responses/NotFound"
        "422":
          $ref: "#/components/responses/ValidationFailed"
    delete:
      summary: Delete planning item
      responses:
        "204":
          description: Deleted
        "401":
          $ref: "#/components/responses/Unauthorized"
        "404":
          $ref: "#/components/responses/NotFound"
  /v1/assets:
    get:
      summary: List assets
      parameters:
        - name: folder_id
          in: query
          required: false
          schema:
            type: string
            format: uuid
        - name: asset_type
          in: query
          required: false
          schema:
            type: string
            enum:
              - image
              - pdf
              - html
              - video
        - name: limit
          in: query
          required: false
          schema:
            type: integer
            default: 25
      responses:
        "200":
          description: Asset list
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AssetListResponse"
        "401":
          $ref: "#/components/responses/Unauthorized"
    post:
      summary: Create asset (two-step upload)
      description: "Creates an asset row with status=pending and returns a one-time
        PUT URL on `www.gibeon.io` where the binary can be streamed. The server
        streams the bytes to R2 and flips status to `ready` on success. Max
        100MB per upload (Cloudflare Worker request-body cap). Allowed
        asset_type values: `image`, `pdf`, `html` — `video` is not yet exposed
        via the public API."
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/CreateAssetBody"
      responses:
        "201":
          description: Asset created + upload URL
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/CreateAssetResponse"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "422":
          $ref: "#/components/responses/ValidationFailed"
  /v1/assets/{id}:
    parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
          format: uuid
    get:
      summary: Get asset
      responses:
        "200":
          description: Asset
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AssetSingleResponse"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "404":
          $ref: "#/components/responses/NotFound"
    patch:
      summary: Update asset
      description: Only `name` and `folder_id` are mutable — file facts (mime_type,
        dimensions, file_size) are derived from the uploaded bytes.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/PatchAssetBody"
      responses:
        "200":
          description: Updated
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AssetSingleResponse"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "404":
          $ref: "#/components/responses/NotFound"
        "422":
          $ref: "#/components/responses/ValidationFailed"
    delete:
      summary: Delete asset
      description: Removes the asset from the database and the R2 object. Returns 409
        `asset_in_use` if the asset is still referenced by a content item.
      responses:
        "204":
          description: Deleted
        "401":
          $ref: "#/components/responses/Unauthorized"
        "404":
          $ref: "#/components/responses/NotFound"
        "409":
          $ref: "#/components/responses/Conflict"
  /v1/assets/{id}/upload:
    parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
          format: uuid
    put:
      summary: Upload asset bytes
      description: Streams the raw file body to R2. `Content-Type` must match the
        `mime_type` declared at create time, and `Content-Length` must be within
        10% of the declared `file_size`. Only allowed while the asset is
        `status=pending`.
      requestBody:
        required: true
        content:
          application/octet-stream:
            schema:
              type: string
              format: binary
      responses:
        "204":
          description: Uploaded — status flipped to ready
        "401":
          $ref: "#/components/responses/Unauthorized"
        "404":
          $ref: "#/components/responses/NotFound"
        "409":
          $ref: "#/components/responses/Conflict"
        "422":
          $ref: "#/components/responses/ValidationFailed"
  /v1/asset-folders:
    get:
      summary: List folders
      responses:
        "200":
          description: Folder list
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/FolderListResponse"
        "401":
          $ref: "#/components/responses/Unauthorized"
    post:
      summary: Create folder
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/CreateFolderBody"
      responses:
        "201":
          description: Created
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/FolderSingleResponse"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "422":
          $ref: "#/components/responses/ValidationFailed"
  /v1/asset-folders/{id}:
    parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
          format: uuid
    patch:
      summary: Update folder
      description: Moving a folder into one of its own descendants returns 422
        `cycle_detected`.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/PatchFolderBody"
      responses:
        "200":
          description: Updated
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/FolderSingleResponse"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "404":
          $ref: "#/components/responses/NotFound"
        "422":
          $ref: "#/components/responses/ValidationFailed"
    delete:
      summary: Delete folder
      description: Returns 409 `folder_not_empty` if the folder contains assets or
        sub-folders.
      responses:
        "204":
          description: Deleted
        "401":
          $ref: "#/components/responses/Unauthorized"
        "404":
          $ref: "#/components/responses/NotFound"
        "409":
          $ref: "#/components/responses/Conflict"
