# Save a dataset def — a persisted query over the episode lake

`POST /v1/datasets/defs`

A dataset is a saved query. Both modes land here — an exact
selection (``query.episodes``, the pairs the user picked) and a dynamic
view (the facets, resolved fresh at every pin). Pin the returned def to
freeze a reproducible version.

- Base URL: `https://junction-api.qualiastudios.dev`
- Authentication: an API key in the `X-API-Key` header, or `Authorization: Bearer …`

## Parameters

- `account_id` (query, string | null (uuid)): Act on this account (active)

## Request body

Required: `application/json` `DatasetDefBody`

## Responses

- `200` Successful Response: `application/json` `any`
- `401` Invalid or missing API key: `application/json` `ErrorResponse`
- `422` Unmodelled or oversized query: `application/json` `ErrorResponse`

## Schemas

```yaml
DatasetDefBody:
  properties:
    name:
      type: string
      maxLength: 128
      minLength: 1
    description:
      type: string
      maxLength: 4096
      default: ""
    query:
      $ref: "#/components/schemas/SavedQueryBody"
  additionalProperties: false
  type: object
  required:
    - name
  title: DatasetDefBody
  description: Save a query over the episode lake as a dataset definition.
SavedQueryBody:
  properties:
    dataset_ids:
      items:
        type: string
        format: uuid
      type: array
      maxItems: 4096
    task:
      type:
        - string
        - "null"
    tier:
      nullable: true
      type: string
      enum:
        - ""
        - retrainable
        - analyze_only
    min_duration_s:
      type:
        - number
        - "null"
    max_duration_s:
      type:
        - number
        - "null"
    tags:
      items:
        type: string
      type: array
      maxItems: 512
    review_state:
      nullable: true
      type: string
      enum:
        - ""
        - unreviewed
        - auto_accepted
        - human_audited
        - rejected
    search:
      type:
        - string
        - "null"
    numeric_predicates:
      items:
        $ref: "#/components/schemas/NumericPredicateBody"
      type: array
      maxItems: 32
    predicates:
      items:
        $ref: "#/components/schemas/ChannelPredicateBody"
      type: array
      maxItems: 32
    episodes:
      items:
        $ref: "#/components/schemas/SavedQueryEpisodeBody"
      type: array
      maxItems: 10000
    query_text:
      type:
        - string
        - "null"
  additionalProperties: false
  type: object
  title: SavedQueryBody
  description: |-
    A dataset IS this query.

    Closed and fully typed on purpose. The query travels to QDS as opaque
    JSON, so this model is the only place its shape is checked — an
    unmodelled key would be stored or dropped in silence and the definition
    would then resolve to a different set of episodes than the one the user
    built. Predicates reuse the explore bodies so a definition can only hold
    a filter the resolver can actually evaluate.

    Quality is not expressible here: no status/flag facet exists, because
    selection is intent. Every episode a user picked belongs to the dataset,
    flagged ones included.
NumericPredicateBody:
  properties:
    field:
      type: string
      enum:
        - duration_s
        - frame_count
        - quality_score
        - speed_mean
        - jerk_mean
        - frozen_action_dims
        - action_dims
        - frozen_state_dims
        - state_dims
        - sync_skew_max_ms
        - sync_skew_ratio
        - sparc
        - ldlj
        - psd_hf_ratio
        - speed_p10
        - speed_p50
        - speed_p90
        - pause_fraction
        - pause_count
        - dt_jitter_ms
        - dt_dropped_frames
    op:
      type: string
      enum:
        - gt
        - lt
        - gte
        - lte
    value:
      type: number
  additionalProperties: false
  type: object
  required:
    - field
    - op
    - value
  title: NumericPredicateBody
ChannelPredicateBody:
  properties:
    stream:
      type: string
      enum:
        - action
        - state
        - telemetry
      default: action
    channel:
      type: string
    agg:
      type: string
      enum:
        - max
        - min
        - mean
        - std
    op:
      type: string
      enum:
        - gt
        - lt
        - gte
        - lte
    value:
      type: number
  additionalProperties: false
  type: object
  required:
    - channel
    - agg
    - op
    - value
  title: ChannelPredicateBody
SavedQueryEpisodeBody:
  properties:
    dataset_id:
      type: string
      format: uuid
    episode_index:
      type: integer
      maximum: 2147483647
      minimum: 0
  additionalProperties: false
  type: object
  required:
    - dataset_id
    - episode_index
  title: SavedQueryEpisodeBody
  description: |-
    One hand-picked member, keyed the way the server keys membership:
    (source dataset, episode index) — not an episode-row uuid.
ErrorResponse:
  properties:
    detail:
      type: string
      description: Error message describing what went wrong
  type: object
  required:
    - detail
  title: ErrorResponse
  description: Standard error response.
```
