> ## Documentation Index
> Fetch the complete documentation index at: https://docs.kiteml.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Dataset augmentations: create, poll, and download runs

> Generate augmented robotics datasets programmatically — point Kite at a dataset, describe the change, and get back a ready-to-train Parquet dataset.

An **augmentation** takes an existing robotics dataset and produces new episodes with a visual change you describe in plain language — a different table surface, new lighting, a swapped background. You give Kite four things; it picks relighting or video augmentation, streams you progress, and delivers a standard LeRobot dataset.

## See it in action

Here is one real demonstration — a bimanual toast-plating task — re-rendered by Augment from a single instruction. Every camera of the episode is transformed together, and the robot's motion and joint trajectories are preserved unchanged. Only the scene changes.

<div className="kite-prompt">
  <span className="kite-prompt__label">Prompt</span>
  <span className="kite-prompt__text">"Replace the white tabletop with warm walnut wood, keep the same lighting and objects."</span>
</div>

<Tabs>
  <Tab title="Left">
    <div className="kite-vs">
      <div className="kite-vs__panel">
        <span className="kite-vs__label">Original</span>

        <video src="https://mintcdn.com/kite-ml/I0tCVEITbvGGR9iI/videos/augment/input_cam_left_high.mp4?fit=max&auto=format&n=I0tCVEITbvGGR9iI&q=85&s=cc7b20f196103eda347548425a8ba3aa" autoPlay loop muted playsInline preload="metadata" data-path="videos/augment/input_cam_left_high.mp4" />
      </div>

      <div className="kite-vs__panel">
        <span className="kite-vs__label kite-vs__label--after">Augmented</span>

        <video src="https://mintcdn.com/kite-ml/I0tCVEITbvGGR9iI/videos/augment/output_cam_left_high.mp4?fit=max&auto=format&n=I0tCVEITbvGGR9iI&q=85&s=64f404dbd91637c71809c623a5d5d48d" autoPlay loop muted playsInline preload="metadata" data-path="videos/augment/output_cam_left_high.mp4" />
      </div>
    </div>
  </Tab>

  <Tab title="Right">
    <div className="kite-vs">
      <div className="kite-vs__panel">
        <span className="kite-vs__label">Original</span>

        <video src="https://mintcdn.com/kite-ml/I0tCVEITbvGGR9iI/videos/augment/input_cam_right_high.mp4?fit=max&auto=format&n=I0tCVEITbvGGR9iI&q=85&s=18d98825f80bf8d3e5f574c05bb60c68" autoPlay loop muted playsInline preload="metadata" data-path="videos/augment/input_cam_right_high.mp4" />
      </div>

      <div className="kite-vs__panel">
        <span className="kite-vs__label kite-vs__label--after">Augmented</span>

        <video src="https://mintcdn.com/kite-ml/I0tCVEITbvGGR9iI/videos/augment/output_cam_right_high.mp4?fit=max&auto=format&n=I0tCVEITbvGGR9iI&q=85&s=b5dd1ee21807bec55041fb8452493440" autoPlay loop muted playsInline preload="metadata" data-path="videos/augment/output_cam_right_high.mp4" />
      </div>
    </div>
  </Tab>

  <Tab title="Left wrist">
    <div className="kite-vs">
      <div className="kite-vs__panel">
        <span className="kite-vs__label">Original</span>

        <video src="https://mintcdn.com/kite-ml/I0tCVEITbvGGR9iI/videos/augment/input_cam_left_wrist.mp4?fit=max&auto=format&n=I0tCVEITbvGGR9iI&q=85&s=46c20e4eaeb0b56459e2e55deb7a0b40" autoPlay loop muted playsInline preload="metadata" data-path="videos/augment/input_cam_left_wrist.mp4" />
      </div>

      <div className="kite-vs__panel">
        <span className="kite-vs__label kite-vs__label--after">Augmented</span>

        <video src="https://mintcdn.com/kite-ml/I0tCVEITbvGGR9iI/videos/augment/output_cam_left_wrist.mp4?fit=max&auto=format&n=I0tCVEITbvGGR9iI&q=85&s=38e5995d2b59739e97c34f918f086725" autoPlay loop muted playsInline preload="metadata" data-path="videos/augment/output_cam_left_wrist.mp4" />
      </div>
    </div>
  </Tab>

  <Tab title="Right wrist">
    <div className="kite-vs">
      <div className="kite-vs__panel">
        <span className="kite-vs__label">Original</span>

        <video src="https://mintcdn.com/kite-ml/I0tCVEITbvGGR9iI/videos/augment/input_cam_right_wrist.mp4?fit=max&auto=format&n=I0tCVEITbvGGR9iI&q=85&s=4fdd27b677e9c670c45c12c03c88b6fb" autoPlay loop muted playsInline preload="metadata" data-path="videos/augment/input_cam_right_wrist.mp4" />
      </div>

      <div className="kite-vs__panel">
        <span className="kite-vs__label kite-vs__label--after">Augmented</span>

        <video src="https://mintcdn.com/kite-ml/I0tCVEITbvGGR9iI/videos/augment/output_cam_right_wrist.mp4?fit=max&auto=format&n=I0tCVEITbvGGR9iI&q=85&s=33d5e3bfdbe0dfdbbb0328bb20efa294" autoPlay loop muted playsInline preload="metadata" data-path="videos/augment/output_cam_right_wrist.mp4" />
      </div>
    </div>
  </Tab>
</Tabs>

<p className="kite-vs__caption">Original teleoperation capture versus the Augment render — same frame, same motion, new environment. One prompt produced all four camera views, each with matching joint trajectories ready to train on.</p>

## Relighting or video augmentation

Kite augments a dataset in one of two ways: **relighting** or **video augmentation**. By default (`"model": "auto"`) a planner looks at your instructions, one frame from each camera, and any reference photos you send, then picks for you. The run reports its choice and the reason in `plan`.

| | Relighting (`relight`) | Video augmentation (`video`) |
| - | - | - |
| Use it for | Lighting mismatches: brightness, exposure, warmth, tint, contrast, saturation | Content changes: textures, materials, objects, backgrounds, new shadows or spotlights |
| What it changes | Colour only. Every pixel stays where it was, so motion and actions stay exact | Generates new video of the scene |
| Episodes | Every episode, full length | The first `episode_count` (up to 50), up to 30 s each |
| Cost | Low, per camera-second | Higher, per camera-second |

Choose one yourself with `"model": "relight"` or `"model": "video"`. Use [`POST /v1/augmentations/estimate`](#estimate-the-cost) to price either before you start.

<Tip>
  If your robot works in the lab but fails on site, compare a training frame with a photo from the deployment camera. A policy trained in daylight often fails under warm evening lamps even though nothing else changed. That gap is exactly what relighting closes, for a fraction of the cost of video augmentation.
</Tip>

## Create a run

One call starts a run. Give it the source dataset, your instructions, the episode count, and where the results should go.

```bash theme={"system"}
curl -X POST https://api.kiteml.com/v1/augmentations \
  -H "Authorization: Bearer $KITE_API_KEY" \
  -H "Kite-Version: 2026-09-27" \
  -H "Content-Type: application/json" \
  -d '{
    "source": { "repo_id": "lerobot/pusht" },
    "instructions": "change the table surface to white marble, vary the lighting",
    "config": { "episode_count": 20 },
    "output": { "type": "download" }
  }'
```

### Request body

<ParamField body="source.repo_id" type="string" required>
  The Hugging Face LeRobot dataset to augment, e.g. `lerobot/pusht`.
</ParamField>

<ParamField body="instructions" type="string" required>
  Plain-language description of the visual change to apply to every episode.
</ParamField>

<ParamField body="model" type="string" default="auto">
  `auto` lets the planner choose; `relight` or `video` picks one yourself. See [Relighting or video augmentation](#relighting-or-video-augmentation).
</ParamField>

<ParamField body="config.episode_count" type="integer" default="3">
  Video augmentation only: how many episodes to generate, from the start of the dataset. Must be between `1` and `50`. Relighting always covers every episode.
</ParamField>

<ParamField body="reference_images" type="object[]">
  Up to 6 photos of the look you want, usually frames from the deployment cameras. Each has `data` (base64, a `data:` URL prefix is fine), `media_type` (`image/jpeg`, `image/png` or `image/webp`) and an optional `camera` (`top` or `observation.images.top`); leave `camera` out to use the photo for every camera. At most 5 MB each and 20 MB in total. Works with `auto` and `relight`.
</ParamField>

<ParamField body="config.relight" type="object">
  Relight parameters you've tuned yourself, keyed by camera or `"*"` for every camera. With `model` left out or set to `auto`, this selects relighting and skips the planner. See [Relight parameters](#relight-parameters).
</ParamField>

<ParamField body="output.type" type="string" default="download">
  Where results are delivered. `download` keeps them on Kite for you to fetch; `huggingface` pushes the finished dataset to your account.
</ParamField>

<ParamField body="output.repo_id" type="string">
  Required when `output.type` is `huggingface` — the destination repo, e.g. `your-org/pusht-marble`.
</ParamField>

The call returns the augmentation resource, including its `id`, immediately:

```json Response — 202 Accepted theme={"system"}
{
  "id": "aug_01J8X4M2K9ZQ6R7T3V5W8Y0B1C",
  "object": "augmentation",
  "status": "processing",
  "progress": 0.0,
  "output": { "type": "download" },
  "created_at": "2026-07-21T09:14:00Z"
}
```

Common failures at create time:

* `400 parameter_invalid` — a malformed field, named in `param` (for example `config.relight` with `"model": "video"`)
* `400 invalid_reference_image` — a reference image that isn't valid base64, isn't an image, or is over 5 MB (20 MB for all images together)
* `400 episode_limit_exceeded` — `episode_count` above 50 for `auto` or `video`
* `400 huggingface_not_connected` — for `huggingface` output, when you haven't linked a Hugging Face token in the dashboard

Credits are charged once the run is planned and the source is read. If your balance is too low at that point, the run ends as `failed` with `error.code` `insufficient_tokens`. Top up and create it again.

See [Authentication → Errors](/platform-api/authentication#errors) for the envelope.

### Idempotency

Pass a unique `Idempotency-Key` header to make retries safe. A repeated request with the same key returns the original run instead of starting a duplicate — so a dropped connection or a CI retry never double-charges you.

```bash theme={"system"}
curl -X POST https://api.kiteml.com/v1/augmentations \
  -H "Authorization: Bearer $KITE_API_KEY" \
  -H "Kite-Version: 2026-09-27" \
  -H "Idempotency-Key: 9f1c8e2a-run-42" \
  -H "Content-Type: application/json" \
  -d '{ "source": { "repo_id": "lerobot/pusht" }, "instructions": "...", "config": { "episode_count": 20 }, "output": { "type": "download" } }'
```

<Note>
  Reusing a key with a *different* payload returns `409 Conflict` — the key is bound to the first request body it saw.
</Note>

## Match your deployment lighting

Send a photo from each deployment camera and let the planner tune a relight for every camera. It previews its settings against your photos before it commits, and it adjusts each camera on its own, because wrist cameras close to a lamp usually look warmer than the overview camera.

<div className="kite-prompt">
  <span className="kite-prompt__label">Prompt</span>
  <span className="kite-prompt__text">"The robot now runs at night under warm ceiling lamps. Match the reference photos."</span>
</div>

<Tabs>
  <Tab title="Top">
    <div className="kite-vs kite-vs--three">
      <div className="kite-vs__panel">
        <span className="kite-vs__label">Original</span>

        <video src="https://mintcdn.com/kite-ml/QD06IA7pouR38SDB/videos/relight/original_top.mp4?fit=max&auto=format&n=QD06IA7pouR38SDB&q=85&s=50f9206e969bdf9634c3a13dca824019" autoPlay loop muted playsInline preload="metadata" data-path="videos/relight/original_top.mp4" />
      </div>

      <div className="kite-vs__panel">
        <span className="kite-vs__label kite-vs__label--after">Relit</span>

        <video src="https://mintcdn.com/kite-ml/QD06IA7pouR38SDB/videos/relight/relit_top.mp4?fit=max&auto=format&n=QD06IA7pouR38SDB&q=85&s=7bd5d6bfa8ad7607caf60dc1c52b09b0" autoPlay loop muted playsInline preload="metadata" data-path="videos/relight/relit_top.mp4" />
      </div>

      <div className="kite-vs__panel">
        <span className="kite-vs__label">Target photo</span>

        <img src="https://mintcdn.com/kite-ml/QD06IA7pouR38SDB/images/relight/target_top.jpg?fit=max&auto=format&n=QD06IA7pouR38SDB&q=85&s=a2c6bdb235ecfc374eb699cce1e45bd6" alt="A photo from the overview camera on site, at night under warm lamps" width="640" height="480" data-path="images/relight/target_top.jpg" />
      </div>
    </div>
  </Tab>

  <Tab title="Left wrist">
    <div className="kite-vs kite-vs--three">
      <div className="kite-vs__panel">
        <span className="kite-vs__label">Original</span>

        <video src="https://mintcdn.com/kite-ml/QD06IA7pouR38SDB/videos/relight/original_left_wrist.mp4?fit=max&auto=format&n=QD06IA7pouR38SDB&q=85&s=644f14ecfe0a214989b270b658d60731" autoPlay loop muted playsInline preload="metadata" data-path="videos/relight/original_left_wrist.mp4" />
      </div>

      <div className="kite-vs__panel">
        <span className="kite-vs__label kite-vs__label--after">Relit</span>

        <video src="https://mintcdn.com/kite-ml/QD06IA7pouR38SDB/videos/relight/relit_left_wrist.mp4?fit=max&auto=format&n=QD06IA7pouR38SDB&q=85&s=1c47d16b243be20c4f5e16e201db41e0" autoPlay loop muted playsInline preload="metadata" data-path="videos/relight/relit_left_wrist.mp4" />
      </div>

      <div className="kite-vs__panel">
        <span className="kite-vs__label">Target photo</span>

        <img src="https://mintcdn.com/kite-ml/QD06IA7pouR38SDB/images/relight/target_left_wrist.jpg?fit=max&auto=format&n=QD06IA7pouR38SDB&q=85&s=4bdc7c72cfaece037848fc201fa90ef4" alt="A photo from the left wrist camera on site, at night under warm lamps" width="640" height="480" data-path="images/relight/target_left_wrist.jpg" />
      </div>
    </div>
  </Tab>
</Tabs>

<p className="kite-vs__caption">Episode 0 of <a href="https://huggingface.co/datasets/kiteml/dual-openyam-close-box">kiteml/dual-openyam-close-box</a>, recorded in daylight and relit through the API with one photo from each camera on site (two of its three cameras shown). The planner chose relighting and tuned every camera to its photo. The robot's motion and every recorded action stay exactly as they were; only the light changes.</p>

Photos are too large to paste into a command line, so build the request body in a file first (this uses [jq](https://jqlang.org)):

```bash theme={"system"}
b64() { base64 < "$1" | tr -d '\n'; }

jq -n \
  --rawfile top   <(b64 live_top.jpg) \
  --rawfile left  <(b64 live_left.jpg) \
  --rawfile right <(b64 live_right.jpg) \
  '{
    source: { repo_id: "kiteml/dual-openyam-close-box" },
    instructions: "The robot now runs at night under warm ceiling lamps. Match the reference photos.",
    reference_images: [
      { camera: "top",         media_type: "image/jpeg", data: $top },
      { camera: "left_wrist",  media_type: "image/jpeg", data: $left },
      { camera: "right_wrist", media_type: "image/jpeg", data: $right }
    ],
    output: { type: "huggingface", repo_id: "your-org/close-box-night" }
  }' > body.json

curl -X POST https://api.kiteml.com/v1/augmentations \
  -H "Authorization: Bearer $KITE_API_KEY" \
  -H "Kite-Version: 2026-09-27" \
  -H "Content-Type: application/json" \
  --data-binary @body.json
```

Or from the CLI:

```bash theme={"system"}
kite augment create --repo-id kiteml/dual-openyam-close-box \
  -i "The robot now runs at night under warm ceiling lamps. Match the reference photos." \
  --reference-image top=live_top.jpg \
  --reference-image left_wrist=live_left.jpg \
  --reference-image right_wrist=live_right.jpg \
  --output huggingface --hf-repo your-org/close-box-night --wait
```

Agents can do the same through the MCP tool `kite_augment_create`, passing `reference_images` as `{ "camera": "top", "path": "live_top.jpg" }` with the local MCP server, or with base64 `data` with the hosted one.

A few seconds after create, the run shows what the planner decided:

```json theme={"system"}
{
  "id": "aug_01J9R2...",
  "status": "processing",
  "model": "relight",
  "config": { "episode_count": 61, "variation_prompts": null, "relight": null },
  "plan": {
    "engine": "relight",
    "reason": "The night-lamp look needs only darker exposure, warm colour temperature and more contrast, all achievable with per-camera colour transforms.",
    "relight": {
      "observation.images.top":         { "exposure": -2.1, "temperature": 4500, "tint": -0.05, "contrast": 0.9, "saturation": 1.3 },
      "observation.images.left_wrist":  { "exposure": -1.7, "temperature": 3800, "tint": -0.05, "contrast": 1.7, "saturation": 1.1 },
      "observation.images.right_wrist": { "exposure": -1.5, "temperature": 3200, "tint": -0.1,  "contrast": 2.0, "saturation": 0.85 }
    },
    "video_instruction": null,
    "source_revision": "b28c65d7e1f94c3a8d2e6b0f5c7a9e1d3b4f6a80"
  }
}
```

`plan.source_revision` is the commit of your dataset the run reads from start to finish, so recording more episodes into the same repository mid-run doesn't change what gets relit. The result is a relit copy of the whole dataset at that commit: same episodes, same length, same actions and timestamps. Only the videos, the camera image statistics and a note on the dataset card change. Train on it together with the original so the policy handles both day and night.

<Note>
  When the planner picks video augmentation instead (say, your photo shows a different table), `plan.video_instruction` holds the concrete edit it derived from your photo, and the run continues as a `video` run.
</Note>

### Relight parameters

Tune a relight yourself, or with your own agent, and skip the planner by sending `config.relight`. With `"*"`, every camera is relit and a camera's own entry overrides it field by field; without `"*"`, cameras you don't name are copied unchanged (and not billed).

```json theme={"system"}
"config": {
  "relight": {
    "*":           { "exposure": -1.5, "temperature": 4200, "contrast": 1.3 },
    "right_wrist": { "exposure": -1.3, "temperature": 3200, "contrast": 1.6 }
  }
}
```

| Parameter | Range | Default | Effect |
| - | - | - | - |
| `exposure` | `-5` to `3` | `0` | Brightness in stops. `-1` halves the light |
| `temperature` | `1500` to `12000` | `6500` | Colour of the light in kelvin. `6500` leaves colour unchanged, `3000` is warm tungsten, `9000` is cool overcast. Changes colour only, not brightness |
| `tint` | `-1` to `1` | `0` | Green (`-`) to magenta (`+`) |
| `contrast` | `0.5` to `2` | `1` | S-curve around mid-grey. Above `1` deepens shadows and lifts highlights |
| `saturation` | `0` to `2` | `1` | Colour intensity |

### Estimate the cost

Both bill per second of video per camera. Relighting bills the full length of every episode on the cameras it changes; video augmentation bills up to 30 s of each episode on every camera. Price a run first with the matching `model`:

```bash theme={"system"}
curl -X POST https://api.kiteml.com/v1/augmentations/estimate \
  -H "Authorization: Bearer $KITE_API_KEY" \
  -H "Kite-Version: 2026-09-27" \
  -H "Content-Type: application/json" \
  -d '{ "model": "relight", "episode_count": 61, "seconds_per_episode": 26.6, "cameras_per_episode": 3 }'
```

```json theme={"system"}
{ "object": "augmentation_estimate", "tokens": 9760, "tokens_per_episode": 160, "assumptions": { "...": "..." } }
```

## Track progress

Episodes are generated and saved incrementally. Poll the run to watch it move through its lifecycle, with a live `progress` value and a human-readable `status_message`.

```bash theme={"system"}
curl https://api.kiteml.com/v1/augmentations/aug_01J8X4... \
  -H "Authorization: Bearer $KITE_API_KEY" \
  -H "Kite-Version: 2026-09-27"
```

```json theme={"system"}
{
  "id": "aug_01J8X4M2K9ZQ6R7T3V5W8Y0B1C",
  "object": "augmentation",
  "status": "processing",
  "progress": 0.42,
  "status_message": "Generated 8 of 20 videos"
}
```

The `status` field moves through:

| Status | Meaning |
| - | - |
| `processing` | Accepted — waiting for a GPU slot, then generating episodes |
| `succeeded` | All episodes generated; output is ready |
| `failed` | The run stopped before completing (see `status_message`) |
| `canceled` | You canceled the run |

<Tip>
  Poll on an interval of a few seconds. Episodes are saved as they finish, so a long run's `progress` moves steadily rather than jumping at the end.
</Tip>

## Get your dataset

When `status` is `succeeded`, a `download` run exposes its files under `output.files`. Fetch each one, preserving its `path`, to reconstruct a standard LeRobot Parquet dataset on disk.

```json Response — output.files theme={"system"}
"output": {
  "type": "download",
  "files": [
    { "path": "meta/info.json",                 "bytes": 3186,  "url": "https://..." },
    { "path": "data/chunk-000/file-000.parquet", "bytes": 16457, "url": "https://..." },
    { "path": "videos/chunk-000/observation.images.cam/episode_000000.mp4", "url": "https://..." }
  ]
}
```

The `kite augment download` CLI command does this for you — see the [CLI docs](https://kiteml.com/docs/cli). If you chose `huggingface` output instead, `output.url` links the dataset pushed to your account.

<Note>
  Each `url` is either a short-lived signed storage URL or an authenticated `/v1/augmentations/:id/files/:path` proxy path. Send your `Authorization` header when fetching and handle both — the proxy path needs the key; the signed URL ignores it.
</Note>

<Check>
  The result is a standard **LeRobot v3.0** dataset: Parquet tables for states and actions plus MP4 camera video. It's the same format Kite training accepts, so you can train on it with no conversion. No proprietary output format, no lock-in.
</Check>

## Cancel a run

Stop a processing run at any time. You're only billed for episodes generated before cancellation.

```bash theme={"system"}
curl -X POST https://api.kiteml.com/v1/augmentations/aug_01J8X4.../cancel \
  -H "Authorization: Bearer $KITE_API_KEY" \
  -H "Kite-Version: 2026-09-27"
```

## The augmentation object

| Field | Type | Description |
| - | - | - |
| `id` | string | `aug_` followed by a time-ordered id |
| `status` | string | `processing`, `succeeded`, `failed`, or `canceled` |
| `progress` | number | `0` to `1` within the current phase (import, generate, publish); it starts again at each phase |
| `status_message` | string or null | What it's doing now, or the last thing it did |
| `error` | object or null | When `failed`: `code` and `message` |
| `model` | string | `auto` until the planner decides, then `relight` or `video` |
| `source` | object | `type` and `repo_id` of the dataset you started from |
| `instructions` | string | Your prompt |
| `config` | object | `episode_count` (for relighting, the dataset's episode count), `variation_prompts`, and `relight` |
| `plan` | object or null | Once planned: `engine` (`relight` or `video`), `reason`, `relight` (parameters per camera), `video_instruction`, and, for relighting, `source_revision` (the dataset commit the run reads) |
| `output` | object | `type` (`download` or `huggingface`). For `huggingface`: `repo_id` and, once `succeeded`, `url`. For `download`: `files` — see below |
| `tokens` | object | `charged`: tokens charged when the work started. Video augmentation refunds episodes it never generates; a failed relight is refunded in full |
| `webhook_metadata` | object or null | Echoed from create |
| `created_at` | string | ISO 8601, UTC |

<Note>
  `output.files` appears only when you retrieve a single `succeeded` augmentation with `GET /v1/augmentations/:id`. List, create, and cancel responses and webhook events leave it out — retrieve the run to get the files.
</Note>

Every field, and every endpoint's parameters and errors, is also under the **API reference** tab.

## Next: train on it

An augmented dataset is a standard LeRobot dataset, so it goes straight into a [training run](/platform-api/training-runs) — same API key, same credit balance, and no conversion step in between.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.