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

# Ditto

> Replace characters, props and backgrounds in a video with Melius Ditto over the REST API.

> [!NOTE]
>
> **Beta** — this feature is rolling out gradually. A team outside the beta
> gets `403` with the code `DITTO_BETA_REQUIRED` from every Ditto endpoint,
> and can join the waitlist from Ditto in the app.

Ditto replaces characters, props and backgrounds in an existing video using reference images. One request can carry up to 30 replacements, and they are all applied together in a single generation with one price and one output video.

Every Ditto endpoint lives under `/api/v1/ditto`:

```
GET  /ditto/models                 → models you can use
GET  /ditto/avatars                → avatars you can cast without uploading an image
POST /ditto                        → price and start a video
GET  /ditto/:generationId  (poll)  → preparing → rendering → finalizing → ready
```

## 1. Upload the video and reference images

Upload the source video (MP4 or MOV) and each reference image with the endpoints under **Uploads** in the API reference: `POST /upload/init`, `POST /upload/sign-part` and `POST /upload/complete`. Keep the `assetId` each upload returns. You can also reuse the asset IDs of files already in your team.

### Use an avatar instead of uploading an image

To cast one of your team's avatars or a Melius avatar, list them with `GET /ditto/avatars`:

```bash theme={null}
curl https://api.melius.com/api/v1/ditto/avatars \
  -H "Authorization: Bearer mel_..."
```

```json theme={null}
{
    "avatars": [
        {
            "kind": "custom",
            "id": "<avatarId>",
            "name": "Jen",
            "imageUrl": "https://...",
            "previewUrl": "https://..."
        },
        {
            "kind": "curated",
            "id": "<avatarId>",
            "name": "Sam",
            "imageUrl": "https://...",
            "previewUrl": "https://..."
        }
    ]
}
```

`custom` avatars are your team's own. `curated` avatars are made by Melius. Send an avatar's `kind` and `id` as a replacement's `avatar`, in place of its `referenceAssetId`:

```json theme={null}
{
    "avatar": { "kind": "curated", "id": "<avatarId>" },
    "targetKind": "character"
}
```

An avatar your team can't use answers `404`, and one whose image isn't made yet answers `409`.

## 2. Start the video

Send the source video and an ordered `replacements` list. Each item has a `referenceAssetId` or an `avatar`, a `targetKind` (`character`, `prop` or `background`) and an optional `targetDescription` naming what it replaces in the video. Starting a video needs an editor role on the team and charges its price to the team.

```bash theme={null}
curl -X POST https://api.melius.com/api/v1/ditto \
  -H "Authorization: Bearer mel_..." \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: <saved UUID>" \
  -d '{
    "sourceAssetId": "<videoAssetId>",
    "replacements": [
      { "referenceAssetId": "<personImageId>", "targetKind": "character" },
      { "referenceAssetId": "<mugImageId>", "targetKind": "prop", "targetDescription": "the mug on the desk" }
    ],
    "maxCredits": 500
  }'
```

A `targetDescription` is optional for characters: Ditto picks the main person, or distinct people in reference order when there are several. It is required when two or more items replace props, or two or more replace backgrounds.

Optional fields:

* `maxCredits` — the most credits the video may cost. A higher price answers `402` and starts nothing.
* `sourceRange` — `{ "startMs": 0, "endMs": 5000 }` to use part of the video. The full video is used otherwise.
* `modelId` and `resolution` — leave them out to use Ditto's default. `GET /ditto/models` lists both.
* `audioMode` — `original` (default) keeps the video's audio; `mute` removes it.
* `additionalInstructions` — extra direction that applies to every replacement. With it, `replacements` can be left out.

The response is `202` with a `generationId` and `status: "preparing"`. When no model can make the video, the response is `400` and its `message` lists the reasons, such as a source longer than the model takes.

### Retry safely

Save an `Idempotency-Key` before sending the request. If you lose the response, resend the identical request with the same key, and you get the original response back instead of a second video. Resending without a key starts another paid video. See [Retry a submission safely](https://docs.melius.com/api/overview.md#retry-a-submission-safely) for the conflict responses.

## 3. Get the result

Poll `GET /ditto/<generationId>` until `status` is `ready` or `failed`. `preparing`, `rendering` and `finalizing` mean it is still working, and `estimatedWait` gives the typical wait in seconds for its model. When it is `ready`, `videoUrl` links to the finished video and `outputAssetId` is its asset in your team. A `failed` video includes a `failureMessage`.

```bash theme={null}
curl https://api.melius.com/api/v1/ditto/<generationId> \
  -H "Authorization: Bearer mel_..."
```


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