# Migrate Robots from Beta to GA

Update Robots requests, response handling, and caption track selection for v1 while keeping existing v0 jobs accessible.

Use this guide to move an existing integration from the Robots Beta API (`/robots/v0`) to the GA API (`/robots/v1`). V1 changes request fields, response shapes, caption track selection, and webhook event types. Changing the API URL alone is not enough.

<Callout type="info" title="Migrate with your coding agent">
  Paste this prompt into your agent to review your integration, apply the relevant changes, and test the migration.

  <div />
</Callout>

The Robots v0 API is deprecated and remains available for six months after the v1 launch. Existing jobs and directives are tied to the API version used to create them. Creating v1 resources does not migrate existing v0 resources.

If you also use Directives or Robots webhooks, follow [Migrate Robots Directives from Beta to GA](/docs/guides/robots-migrate-directives-to-v1) and [Migrate Robots webhooks from Beta to GA](/docs/guides/robots-migrate-webhooks-to-v1). See the <ApiRefLink href="/docs/api-reference/robots/jobs">Robots API reference</ApiRefLink> for the full contracts.

## Plan your rollout

During the migration, v0 and v1 run side by side. Jobs and Directives you already have stay on v0, while anything you create through v1 returns v1 response shapes and sends v1 webhook events. Follow these steps in order so your integration can handle both versions at every stage, and so you can switch new requests back to v0 until the cutover is complete.

1. Update and deploy webhook handlers that accept both versions **before** creating v1 jobs or Directives.
2. Update request construction and response parsing using the mappings below. Store the API version that created each job or Directive alongside its ID.
3. Test v1 with representative assets, including assets with several caption tracks and any audio-only content you process.
4. Switch new job creation to v1. Continue reading and acting on existing v0 jobs through v0.
5. Create v1 replacements for any v0 Directives, then update the integrations that reference their IDs.
6. Keep v0 webhook handling while v0 Directives are still running or v0 webhook deliveries are still arriving.

<Callout type="warning">
  V0 and v1 don't recognize each other's jobs as duplicates. Submitting the same job to both runs it twice, and both runs consume units. When comparing versions, use a test asset and review any options that write results back to the asset.
</Callout>

## Update request paths and parameters

Change new job requests from `/robots/v0/jobs/{workflow}` to `/robots/v1/jobs/{workflow}`. Mux API authentication, access tokens, and the `parameters` request envelope stay the same.

For example, key-moment duration preferences change from integer milliseconds to seconds.

**V0 request:**

```json
{
  "parameters": {
    "asset_id": "YOUR_ASSET_ID",
    "target_duration_ms": { "min": 15000, "max": 45000 }
  }
}
```

**V1 request:**

```json
{
  "parameters": {
    "asset_id": "YOUR_ASSET_ID",
    "target_duration": { "min": 15, "max": 45 }
  }
}
```

Update these fields wherever you construct workflow requests, including [Directive binding parameters](/docs/guides/robots-migrate-directives-to-v1#convert-the-configuration).

| Workflow | V0 field | V1 action |
| :-- | :-- | :-- |
| `find-key-moments` | `target_duration_ms.min` / `.max` | Use `target_duration.min` / `.max`, dividing values by 1,000. Fractional seconds with millisecond precision are supported. |
| `translate-audio`, `translate-captions` | `to_language_code` | Rename to `output_language_code`. |
| `summarize`, `generate-chapters` | `prompt_overrides` | Remove it and express supported preferences through `output_steering`. This is not a one-to-one prompt conversion. |
| `generate-premium-captions` | `replace_existing` | Use `replace_existing_tracks`: `true` maps to `"replace_all"`; `false` maps to `"fail"`. |
| `edit-captions` | `delete_original_track` | Choose `replace_existing_tracks`: `"replace"` replaces the source, and `"fail"` keeps it and creates a separately named track. |
| `edit-captions` | `track_name_suffix` | Supply the complete desired `track_name` instead. |

For edit-captions, preserving the source requires `replace_existing_tracks: "fail"` and a `track_name` different from the source track's name. The default v1 replacement policy is `"replace"`. Review this explicitly if your v0 integration used the legacy naming or deletion options.

Unknown request fields are ignored, rather than treated as aliases. For example, sending only `to_language_code` to a v1 translation endpoint does not satisfy the required `output_language_code` field. Remove obsolete fields from your integration even when the request succeeds without them.

## Update response parsing

These changes apply to both a job's `GET` response and the `data` object of its v1 webhook.

### Read media times as seconds

Media offsets in v1 use seconds and can contain fractional values. These are positions on the original asset timeline, not Unix timestamps. A value of `23.456` represents the same position as `23456` milliseconds in v0.

| Workflow / output | V0 | V1 |
| :-- | :-- | :-- |
| `find-best-thumbnails`, `outputs.best_thumbnails[]` | `timestamp_ms` | `time` |
| `find-key-moments`, `outputs.moments[]` and nested `cues[]` | `start_ms`, `end_ms` | `start_time`, `end_time` |
| `find-key-moments`, `quotable_segment` | `start_ms` | `time` |
| `generate-engagement-insights`, `outputs.moment_insights[]` | `start_ms`, `end_ms` | `start_time`, `end_time` |

Remove any existing millisecond-to-second conversion when reading v1 values, or playback positions will be scaled twice. Unix timestamps such as `created_at` and `updated_at` already use seconds and do not need this conversion. Chapter start times already use seconds too.

### Read track IDs and asset update outcomes

| Workflow / output | V0 | V1 |
| :-- | :-- | :-- |
| `edit-captions`, `translate-captions`, `translate-audio` | `outputs.uploaded_track_id` | `outputs.track_id` |
| Workflows reporting deleted tracks | `replaced_tracks: [{ "id": "TRACK_ID", ... }]` | `replaced_track_ids: ["TRACK_ID"]` |
| `generate-premium-captions`, legacy alias | `outputs.replaced_track_id` | `outputs.replaced_track_ids[]` |
| `generate-premium-captions` | `outputs.detected_language` | `outputs.detected_language_code` |
| `generate-chapters`, legacy alias | `outputs.asset_update` | `outputs.asset_updates.chapters` |
| `generate-chapters`, nested replacement outcome | `asset_updates.chapters.replaced_track_id` | `asset_updates.chapters.replaced_track_ids[]` |

`replaced_track_ids` contains IDs of tracks that were deleted. It is omitted when no deletions were recorded; do not assume it is always present. V1 no longer includes the metadata objects previously returned in `replaced_tracks`.

In caption editing and translation, `parameters.track_id` is the **source** track, while `outputs.track_id` identifies the **newly created** track.

Writing results back to the asset is best effort. A job can complete successfully while an asset update reports `status: "failed"`, so check the corresponding `asset_updates` entry separately from the job status. Older jobs that recorded no update outcome omit it. For chapter replacement, a failed update with deleted track IDs means deletion succeeded but creation failed.

## Review caption track selection

V1 uses the first ready caption track in asset track order, without preferring English. If your integration relied on the English preference, supply `language_code` explicitly when the workflow supports it.

An explicit source `language_code` must match exactly, including case and region. For example, `en` does not select an `en-US` caption track. When checking for an existing track in a translation's target language, or a track to replace, `en` and `en-US` count as the same language.

An explicit caption `track_id` must identify a ready caption track. Audio-only assets with exactly one ready text track that isn't marked as captions can use that track as a legacy transcript. Audio-only moderation no longer assumes `language_code: "en"` when you omit it.

Before switching production requests, test with assets that have multiple languages, text tracks that aren't captions, or captions that are still preparing. [Jobs dispatched by a Directive](/docs/guides/robots-migrate-directives-to-v1#test-and-switch-automatic-triggers) follow the selection rules of the Directive's version.

## Replace Find Scenes integrations

`find-scenes` is not included in the v1 release. Use [Analyze Structure](/docs/guides/robots-analyze-structure) (`POST /robots/v1/jobs/analyze-structure`) and read `outputs.scenes`. Analyze Structure also produces a shared title, summary, tags, chapters, and key moments.

```bash
curl https://api.mux.com/robots/v1/jobs/analyze-structure \
  -u "$MUX_TOKEN_ID:$MUX_TOKEN_SECRET" \
  -H "Content-Type: application/json" \
  -d '{ "parameters": { "asset_id": "YOUR_ASSET_ID" } }'
```

Update your scene parser for `start_time` and `end_time` in seconds, including nested cue and shot intervals. Review the Analyze Structure contract rather than copying Find Scenes parameters unchanged: its output steering and result structure are different, and generated scene boundaries can differ.

## Keep existing jobs on the version that created them

The v1 jobs list can discover jobs created through either version. Each summary includes `_links.self.href`, which points to the route for the version that created the job:

```json
{
  "id": "rjob_example",
  "_links": {
    "self": { "href": "/robots/v0/jobs/summarize/rjob_example" }
  }
}
```

This is an excerpt from a v1 list response. Follow the relative URL against `https://api.mux.com` rather than rebuilding it with `/v1`. List summaries do not contain full workflow parameters or outputs.

Detail and cancel requests must use the version that created the job. A v0 job requested through a v1 detail route returns `404`; it is not converted into a v1 job on read. For cancellation, use `POST /robots/{VERSION}/jobs/{JOB_ID}/cancel` with that version.

Store the version that created each new job, and keep it in polling queues, saved job links, and retry/cancel actions. A v1 webhook identifies a v1-created job; a v0 webhook identifies a v0-created job.

## Update error handling

Read `error.messages[]` instead of the removed `error.message` field. For example:

```json
{
  "error": {
    "type": "track_not_ready",
    "messages": ["No ready subtitle track available for this asset"]
  }
}
```

Handle errors according to their type and status rather than matching message strings.

| Situation | V1 response | Migration action |
| :-- | :-- | :-- |
| Malformed JSON | `400` | Correct the request encoding. |
| Invalid recognized parameters or malformed IDs | `422 validation_error` | Correct the values or ID. |
| Invalid Mux credentials | `401 unauthorized` | Check the credentials. |
| Preparing asset or required caption track | `409 asset_not_ready` or `409 track_not_ready` | Wait for the resource to become ready before retrying. |
| Duplicate active job | `409 conflict` | Track the existing job instead of creating another. |
| Errored asset or unusable caption track | `422` | Correct the source; repeated readiness retries will not resolve it. |
| Missing resource or wrong API version | `404` | Check the ID, environment, and the API version that created the resource. |

These readiness and duplicate-job types describe job creation. Other operations can return `409` for operation-specific conflicts, such as cancelling a job already in a terminal state.

## Migration checklist

* \[ ] New job requests use `/robots/v1` and the renamed parameters.
* \[ ] Request builders no longer send removed fields.
* \[ ] Response parsers read seconds, v1 track IDs, and `error.messages[]`.
* \[ ] Caption track tests cover your actual languages and track order.
* \[ ] Find Scenes integrations use Analyze Structure and its result schema.
* \[ ] Job lists follow `_links.self`, and stored jobs keep the API version that created them.
* \[ ] Webhook handlers that accept both versions are deployed before v1 creation starts.
* \[ ] V0 Directives have v1 replacements. See [Migrate Robots Directives from Beta to GA](/docs/guides/robots-migrate-directives-to-v1).
