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

# Virtual Try-On

> Dress people in live or recorded video with text prompts, garment images, or both.

Try on clothing and accessories in realtime. Stream a live camera feed, describe the outfit or provide a reference image of the garment, and Lucy VTON will realistically dress the subject while preserving motion and body shape — all with minimal latency.

## Quick start (Realtime)

<CodeGroup>
  ```tsx JavaScript SDK theme={null}
  import { createDecartClient, models } from "@decartai/sdk";

  const model = models.realtime("lucy-vton-latest");

  const stream = await navigator.mediaDevices.getUserMedia({
    audio: true,
    video: {
      frameRate: model.fps,
      width: model.width,
      height: model.height,
    },
  });

  const client = createDecartClient({ apiKey: "your-api-key-here" });

  const realtimeClient = await client.realtime.connect(stream, {
    model,
    mirror: "auto",
    onRemoteStream: (editedStream) => {
      document.querySelector("#video-output").srcObject = editedStream;
    },
  });

  // Try on with a garment image and descriptive prompt
  const garmentFile = document.querySelector('input[type="file"]').files[0];
  await realtimeClient.set({
    prompt: "Substitute the current top with a navy blue hoodie with a white cross logo on the chest",
    image: garmentFile,
    enhance: false,
  });

  // Disconnect when done
  realtimeClient.disconnect();
  ```

  ```python Python SDK theme={null}
  from decart import models
  from decart.realtime import RealtimeClient, RealtimeConnectOptions
  from decart.types import ModelState

  model = models.realtime("lucy-vton-latest")

  realtime_client = await RealtimeClient.connect(
      api_key="your-api-key-here",
      stream=camera_stream,
      options=RealtimeConnectOptions(
          model=model,
          initial_state=ModelState(
              prompt="Substitute the current top with a red leather jacket with a zip front",
          ),
          on_remote_stream=lambda edited_stream: handle_stream(edited_stream),
      ),
  )

  # Change outfit on the fly
  await realtime_client.set_prompt("Substitute the current top with a blue denim jacket with a relaxed fit")

  # Disconnect when done
  await realtime_client.disconnect()
  ```

  ```kotlin Android SDK theme={null}
  import ai.decart.sdk.DecartClient
  import ai.decart.sdk.DecartClientConfig
  import ai.decart.sdk.RealtimeModels
  import ai.decart.sdk.realtime.ConnectOptions
  import ai.decart.sdk.realtime.InitialPrompt

  val client = DecartClient(context, DecartClientConfig(apiKey = "your-api-key"))
  val realtime = client.realtime

  realtime.initialize(eglBase)

  realtime.connect(
      localVideoTrack = cameraTrack,
      localAudioTrack = null,
      options = ConnectOptions(
          model = RealtimeModels.LUCY_2_1_VTON,
          onRemoteVideoTrack = { track ->
              remoteRenderer.addSink(track)
          },
          initialPrompt = InitialPrompt("Substitute the current top with a red leather jacket with a zip front")
      )
  )

  // Change outfit on the fly
  realtime.setPrompt("Substitute the current top with a blue denim jacket with a relaxed fit", enhance = true)
  ```

  ```swift Swift SDK theme={null}
  import DecartSDK

  let config = DecartConfiguration(apiKey: "your-api-key-here")
  let client = DecartClient(decartConfiguration: config)

  let model = Models.realtime(.lucy2_1_vton)

  let realtimeManager = try await client.realtime.connect(
      stream: cameraStream,
      model: model,
      initialState: .init(prompt: "Substitute the current top with a red leather jacket with a zip front")
  ) { editedStream in
      // Display the edited video
      videoView.stream = editedStream
  }

  // Change outfit on the fly
  try await realtimeManager.setPrompt("Substitute the current top with a blue denim jacket with a relaxed fit")
  ```
</CodeGroup>

### Realtime Parameters

* **`prompt`** — Text description of the outfit change. Use the **substitute** or **add** patterns described in the [prompting guide](#prompting-guide) below.
* **`image`** — A garment reference image to guide the try-on. Supported formats: JPEG, PNG, WebP.
* **`enhance`** — Whether to auto-enhance the prompt (default: `true`). Set to `false` when you provide detailed prompts yourself.

<Tip>`set()` replaces the entire state — fields you omit are cleared. Always include every field you want to keep.</Tip>

### Using Reference Images

For the best results, provide a **garment reference image** paired with a **descriptive prompt**. This gives the model the clearest signal of what to apply.

```tsx theme={null}
// Best approach: reference image + descriptive prompt
await realtimeClient.set({
  prompt: "Substitute the current top with a black bomber jacket with a blue logo on the chest and a zip front",
  image: garmentFile,
  enhance: false,
});

// Reference image only — the model dresses the person to match
await realtimeClient.set({ image: garmentFile });
```

**Reference image tips:**

* **Clean garment images work best** — just the clothing item, no person wearing it
* **White or plain backgrounds** are ideal
* **At least 512×512 pixels** — the model reproduces what it sees, so a clear image produces better results
* If your source image shows a person wearing the garment, consider using an image editing model to extract just the clothing item first

### Dynamic Outfit Changes

Switch outfits instantly during a live session — call `setPrompt()` or `set()` again at any time without reconnecting:

```tsx theme={null}
// Start with a casual look
realtimeClient.setPrompt("Substitute the current top with a grey crewneck sweatshirt with a large blue logo on the front");

// Switch to formal
realtimeClient.setPrompt("Substitute the current top with a navy blue blazer, worn over a light blue dress shirt");

// Try an accessory
realtimeClient.setPrompt("Add a black baseball cap with a blue logo to the person's head");
```

### Client Token Security

In production, never expose your permanent API key (`dct_*`) to the browser. Instead, create a short-lived **client token** on your server and pass it to the frontend:

```typescript theme={null}
// Server: app/api/tokens/route.ts
import { createDecartClient } from "@decartai/sdk";

export async function POST() {
  const client = createDecartClient({ apiKey: process.env.DECART_API_KEY });
  const token = await client.tokens.create();
  return Response.json(token);
}
```

```typescript theme={null}
// Client: fetch token, then connect
const res = await fetch("/api/tokens", { method: "POST" });
const { apiKey } = await res.json();

const client = createDecartClient({ apiKey }); // short-lived ephemeral token
const rtClient = await client.realtime.connect(stream, { model, onRemoteStream });
```

<Note>
  Client tokens have a 10-minute TTL. Create a new token each time a user opens a try-on session. Active WebRTC sessions continue working even after the token expires. See the [Client Tokens guide](/getting-started/client-tokens) for details.
</Note>

## Prompting Guide

Lucy VTON works best with structured prompts that follow a **substitute** or **add** pattern. Focus on what needs to change — you don't need to describe the entire scene.

### Prompt Patterns

| Pattern        | When to use                               | Example                                                                          |
| -------------- | ----------------------------------------- | -------------------------------------------------------------------------------- |
| **Substitute** | Replacing an existing garment             | `"Substitute the current top with a red plaid flannel shirt with a relaxed fit"` |
| **Add**        | Adding something the person isn't wearing | `"Add a wide-brimmed straw hat to the person's head"`                            |

When you don't know the person's current outfit, use generic references: `"the current top"`, `"the current bottoms"`, `"the current headwear"`.

### Example Prompts

```
✅ "Substitute the current top with a bright red hoodie with an oversized casual fit"
✅ "Substitute the current top with a black leather bomber jacket with ribbed cuffs"
✅ "Substitute the current top with a navy blue polo shirt with a white logo on the chest"
✅ "Add a navy baseball cap with a blue logo to the person's head"
✅ "Substitute the current bottoms with dark blue slim-fit jeans"

❌ "Put a jacket on the person"        (too vague — no color, material, or fit)
❌ "Red hoodie"                         (missing action and context)
❌ "Wearing a jacket"                   (no detail about the garment)
```

### Prompt Tips

* **Be specific** — include color, material, texture, pattern, and fit. Aim for **20–30 words**.
* **Describe only what you see** — don't guess details like zippers or pockets unless they're clearly visible in the reference image.
* **One garment per prompt** — combining multiple unrelated changes can produce unpredictable results.
* **Reference image + prompt** — when you have a garment image, always pair it with a descriptive prompt for maximum control.
* **Use `enhance: true` as a fallback** — the built-in enhance option can improve short or vague prompts automatically, but detailed prompts you write yourself will produce the best results.

### Generating Prompts with an LLM

For user-uploaded garment images where you don't have a pre-written prompt, you can use any vision LLM (GPT-4o-mini, Claude, Gemini) to auto-generate a descriptive prompt from the garment image:

```typescript theme={null}
// Send the garment image (and optionally a camera frame) to your LLM
const formData = new FormData();
formData.append("image", garmentBlob);
formData.append("personFrame", cameraFrameBlob); // optional — improves accuracy

const res = await fetch("/api/enhance-prompt", { method: "POST", body: formData });
const { prompt } = await res.json();
// → "Substitute the grey crewneck sweater with a blue and pink flame print hoodie with an oversized fit"
```

<Tip>
  Sending a camera frame of the person gives the LLM context about what they're currently wearing, so it generates more accurate prompts (e.g. "Substitute the grey sweater" instead of generic "Substitute the current top").
</Tip>

### Extracting Clothing from Model Photos

When users upload photos from fashion websites where a model is wearing the garment, the image contains a person and background rather than a clean garment shot. Use an image editing model to extract just the clothing item on a white background before sending it to the try-on model.

## Batch API

Lucy VTON is also available as a batch/queue model for processing pre-recorded videos asynchronously. Submit a job, poll for completion, then download the result.

<CodeGroup>
  ```bash Curl theme={null}
  # Submit job
  JOB_ID=$(curl -s -X POST https://api.decart.ai/v1/jobs/lucy-2.1-vton \
    -H "X-API-KEY: $DECART_API_KEY" \
    -F "data=@person.mp4" \
    -F "prompt=Substitute the current top with a red leather jacket with a zip front" \
    -F "reference_image=@garment.jpg" | jq -r '.job_id')

  # Poll until completed
  while true; do
    STATUS=$(curl -s -H "X-API-KEY: $DECART_API_KEY" \
      https://api.decart.ai/v1/jobs/$JOB_ID | jq -r '.status')
    echo "Status: $STATUS"
    [ "$STATUS" = "completed" ] && break
    [ "$STATUS" = "failed" ] && exit 1
    sleep 2
  done

  # Download result
  curl -H "X-API-KEY: $DECART_API_KEY" \
    https://api.decart.ai/v1/jobs/$JOB_ID/content --output output.mp4
  ```

  ```javascript JavaScript SDK theme={null}
  import { createDecartClient, models } from "@decartai/sdk";
  import { readFileSync, writeFileSync } from "fs";

  const client = createDecartClient({ apiKey: "your-api-key-here" });

  const videoBuffer = readFileSync("person.mp4");
  const videoBlob = new Blob([videoBuffer], { type: "video/mp4" });

  const imageBuffer = readFileSync("garment.jpg");
  const imageBlob = new Blob([imageBuffer], { type: "image/jpeg" });

  const result = await client.queue.submitAndPoll({
    model: models.video("lucy-2.1-vton"),
    prompt: "Substitute the current top with a red leather jacket with a zip front",
    data: videoBlob,
    reference_image: imageBlob,
    onStatusChange: (job) => console.log(`Status: ${job.status}`),
  });

  if (result.status === "completed") {
    const buffer = Buffer.from(await result.data.arrayBuffer());
    writeFileSync("output.mp4", buffer);
  }
  ```

  ```python Python SDK theme={null}
  import asyncio
  from decart import DecartClient, models

  async def virtual_try_on():
      async with DecartClient(api_key="your-api-key-here") as client:
          with open("person.mp4", "rb") as video_file:
              with open("garment.jpg", "rb") as garment_image:
                  result = await client.queue.submit_and_poll({
                      "model": models.video("lucy-2.1-vton"),
                      "prompt": "Substitute the current top with a red leather jacket with a zip front",
                      "data": video_file,
                      "reference_image": garment_image,
                      "on_status_change": lambda job: print(f"Status: {job.status}"),
                  })

          if result.status == "completed":
              with open("output.mp4", "wb") as f:
                  f.write(result.data)

  asyncio.run(virtual_try_on())
  ```
</CodeGroup>

### Batch Parameters

* `data` (**required**) — Input video of the person to dress.
* `prompt` (**optional**) — Text description of the outfit change. Use the substitute/add patterns above. Defaults to empty string.
* `reference_image` (**optional**) — An image of the garment or accessory to try on.
* `resolution` (**optional**) — Output resolution: `720p` (default).
* `seed` (**optional**) — Seed for reproducible generation (0–4294967295).
* `enhance_prompt` (**optional**) — Whether to enhance the prompt (default: `true`).

### Video Requirements

* **Format:** MP4 (H.264 or VP8 codec)
* **Aspect Ratio:** 16:9 (landscape) or 9:16 (portrait)
* **File Size:** Maximum 200MB
* **Output Resolution:** 720×1280 (portrait) or 1280×720 (landscape)

## Next steps

<CardGroup>
  <Card icon="cube" title="Try in the Platform" href="https://platform.decart.ai/models/lucy-edit-live">
    Try virtual try-on in the interactive platform.
  </Card>

  <Card icon="code" title="Batch API Reference" href="/api-reference/lucy-21-vton">
    Full API documentation for the batch virtual try-on endpoint.
  </Card>

  <Card icon="bolt" title="Streaming Best Practices" href="/models/realtime/streaming-best-practices">
    Optimize your realtime integration for the best experience.
  </Card>

  <Card icon="github" title="Try-On Examples" href="https://github.com/DecartAI/tryon-examples">
    Production-ready examples: e-commerce, digital mirror, outfit builder, and more.
  </Card>
</CardGroup>
