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

# Remix

# Remix Video

Create a new video based on an existing video, inheriting its settings with a new prompt.

```http theme={null}
POST /v1/videos/:video_id/remix
```

## Overview

Remixing creates a new video generation job that inherits the source video's:

* Model (`sora-2` or `sora-2-pro`)
* Duration (`seconds`)
* Resolution (`size`)
* Theory of Change position (if set)

You only need to provide a new `prompt`. This is useful for:

* Iterating on a concept with different prompts
* Creating variations of successful videos
* Maintaining consistent visual settings across a campaign

<Info>
  Remixing costs the same as creating a new video with the same settings. Credits are escrowed and charged normally.
</Info>

## Request

### Path Parameters

| Parameter  | Type   | Required | Description                   |
| ---------- | ------ | -------- | ----------------------------- |
| `video_id` | string | Yes      | Source video ID to remix from |

### Headers

| Header          | Value                | Required |
| --------------- | -------------------- | -------- |
| `Authorization` | `Bearer oc_live_...` | Yes      |
| `Content-Type`  | `application/json`   | Yes      |

### Body Parameters

| Parameter | Type   | Required | Description                               |
| --------- | ------ | -------- | ----------------------------------------- |
| `prompt`  | string | Yes      | New video description (10-500 characters) |

## Response

### Success (202 Accepted)

Returns the created remix video object:

```json theme={null}
{
  "id": "video_xyz789",
  "object": "video",
  "created": 1730638000,
  "model": "sora-2",
  "prompt": "Activists celebrating victory at city hall",
  "status": "queued",
  "progress": 0,
  "seconds": "8",
  "size": "1280x720",
  "url": null,
  "x-cost": {
    "amount": 2.50,
    "currency": "USD"
  },
  "x-remixed-from": "video_abc123"
}
```

The `x-remixed-from` field links to the source video ID.

## Examples

### Basic Remix

<CodeGroup>
  ```typescript TypeScript theme={null}
  import OpenAI from 'openai';

  const client = new OpenAI({
    apiKey: process.env.OUTCRY_API_KEY,
    baseURL: 'https://api.outcryai.com/v1'
  });

  // Original video
  const original = await client.videos.retrieve('video_abc123');
  console.log(`Original: "${original.prompt}"`);
  console.log(`Settings: ${original.model}, ${original.seconds}s, ${original.size}`);

  // Create remix with new prompt
  const remix = await client.videos.create({
    // ... settings inherited automatically
    prompt: 'Activists celebrating policy victory'
  });
  // NOTE: OpenAI SDK doesn't have a .remix() method,
  // so you'll need to use fetch() or create a custom method

  // Or use fetch directly:
  const response = await fetch(
    `https://api.outcryai.com/v1/videos/${original.id}/remix`,
    {
      method: 'POST',
      headers: {
        'Authorization': `Bearer ${process.env.OUTCRY_API_KEY}`,
        'Content-Type': 'application/json'
      },
      body: JSON.stringify({
        prompt: 'Activists celebrating policy victory'
      })
    }
  );

  const remix = await response.json();

  console.log(`Remix: "${remix.prompt}"`);
  console.log(`Inherited: ${remix.model}, ${remix.seconds}s, ${remix.size}`);
  ```

  ```python Python theme={null}
  from openai import OpenAI
  import requests
  import os

  client = OpenAI(
      api_key=os.environ.get("OUTCRY_API_KEY"),
      base_url="https://api.outcryai.com/v1"
  )

  # Original video
  original = client.videos.retrieve("video_abc123")
  print(f"Original: \"{original.prompt}\"")
  print(f"Settings: {original.model}, {original.seconds}s, {original.size}")

  # Create remix
  response = requests.post(
      f"https://api.outcryai.com/v1/videos/{original.id}/remix",
      headers={
          "Authorization": f"Bearer {os.environ.get('OUTCRY_API_KEY')}",
          "Content-Type": "application/json"
      },
      json={"prompt": "Activists celebrating policy victory"}
  )

  remix = response.json()

  print(f"Remix: \"{remix['prompt']}\"")
  print(f"Inherited: {remix['model']}, {remix['seconds']}s, {remix['size']}")
  ```

  ```bash curl theme={null}
  # Get original video settings
  curl https://api.outcryai.com/v1/videos/video_abc123 \
    -H "Authorization: Bearer oc_live_..."

  # Create remix with new prompt
  curl https://api.outcryai.com/v1/videos/video_abc123/remix \
    -H "Authorization: Bearer oc_live_..." \
    -H "Content-Type: application/json" \
    -d '{
      "prompt": "Activists celebrating policy victory"
    }'
  ```
</CodeGroup>

### Create Multiple Variations

```typescript theme={null}
async function createVariations(
  sourceVideoId: string,
  prompts: string[]
): Promise<Video[]> {
  const remixes: Video[] = [];

  for (const prompt of prompts) {
    const response = await fetch(
      `https://api.outcryai.com/v1/videos/${sourceVideoId}/remix`,
      {
        method: 'POST',
        headers: {
          'Authorization': `Bearer ${process.env.OUTCRY_API_KEY}`,
          'Content-Type': 'application/json'
        },
        body: JSON.stringify({ prompt })
      }
    );

    const remix = await response.json();
    remixes.push(remix);

    console.log(`Created remix: ${remix.id} - "${prompt}"`);

    // Wait to avoid rate limits
    await new Promise(resolve => setTimeout(resolve, 1000));
  }

  return remixes;
}

const variations = await createVariations('video_abc123', [
  'Activists marching with banners',
  'Activists speaking at rally',
  'Activists organizing community meeting',
  'Activists celebrating victory'
]);

console.log(`Created ${variations.length} variations`);
```

### Iterative Refinement

```typescript theme={null}
async function iterateOnPrompt(
  initialPrompt: string,
  refinements: string[]
): Promise<Video[]> {
  // Create initial video
  const initial = await client.videos.create({
    model: 'sora-2',
    prompt: initialPrompt,
    seconds: '8',
    size: '1280x720'
  });

  console.log(`Initial: ${initial.id}`);

  // Wait for completion
  let current = await waitForVideo(initial.id);

  const videos = [current];

  // Create refinements as remixes
  for (const refinedPrompt of refinements) {
    const response = await fetch(
      `https://api.outcryai.com/v1/videos/${current.id}/remix`,
      {
        method: 'POST',
        headers: {
          'Authorization': `Bearer ${process.env.OUTCRY_API_KEY}`,
          'Content-Type': 'application/json'
        },
        body: JSON.stringify({ prompt: refinedPrompt })
      }
    );

    current = await response.json();
    current = await waitForVideo(current.id);

    videos.push(current);

    console.log(`Refined: ${current.id} - "${refinedPrompt}"`);
  }

  return videos;
}

const iterations = await iterateOnPrompt(
  'Climate protest at city hall',
  [
    'Climate protest with large crowd at city hall',
    'Climate protest with diverse activists and banners at city hall',
    'Powerful climate protest with thousands of activists at city hall'
  ]
);
```

## What Gets Inherited

When you remix a video, the following settings are **automatically inherited**:

| Setting             | Inherited      | Can Override |
| ------------------- | -------------- | ------------ |
| `model`             | ✅ Yes          | ❌ No         |
| `seconds`           | ✅ Yes          | ❌ No         |
| `size`              | ✅ Yes          | ❌ No         |
| `x-theory-position` | ✅ Yes (if set) | ❌ No         |
| `x-style`           | ✅ Yes (if set) | ❌ No         |
| `prompt`            | ❌ No           | ✅ Required   |

<Warning>
  You **cannot** override inherited settings. To change the model, duration, or resolution, create a new video instead of remixing.
</Warning>

## Cost and Billing

### Same Cost as Original

Remixes cost the same as creating a new video with the same settings:

```typescript theme={null}
// Original video: sora-2, 8s, 1280x720 = $2.50
const original = await client.videos.retrieve('video_abc123');

// Remix: same settings = same cost ($2.50)
const remix = await fetch(
  `https://api.outcryai.com/v1/videos/${original.id}/remix`,
  {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${process.env.OUTCRY_API_KEY}`,
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({
      prompt: 'New variation'
    })
  }
).then(r => r.json());

// Both cost $2.50 each
```

### Credits Escrowed

Remix credits are escrowed like normal video creation:

1. Check balance before remix
2. Escrow credits when remix starts
3. Finalize escrow when remix completes
4. Refund escrow if remix fails

## Error Responses

### 404 Not Found - Source Video Doesn't Exist

```json theme={null}
{
  "error": {
    "type": "invalid_request_error",
    "message": "Source video not found: video_abc123",
    "code": "video_not_found"
  }
}
```

### 400 Bad Request - Invalid Prompt

```json theme={null}
{
  "error": {
    "type": "invalid_request_error",
    "message": "prompt must be 10-500 characters",
    "param": "prompt",
    "code": "invalid_prompt"
  }
}
```

**Solution:** Validate prompt length:

```typescript theme={null}
function validatePrompt(prompt: string): void {
  if (prompt.length < 10) {
    throw new Error('Prompt must be at least 10 characters');
  }
  if (prompt.length > 500) {
    throw new Error('Prompt must be less than 500 characters');
  }
}
```

### 402 Payment Required - Insufficient Credits

```json theme={null}
{
  "error": {
    "type": "insufficient_quota",
    "message": "Insufficient prepaid balance. Required: $2.50, Available: $1.25",
    "code": "insufficient_quota"
  }
}
```

**Solution:** Add credits before remixing.

## Remix Chains

Track remix lineage:

```typescript theme={null}
async function getRemixChain(videoId: string): Promise<Video[]> {
  const chain: Video[] = [];
  let currentId: string | null = videoId;

  while (currentId) {
    const video = await client.videos.retrieve(currentId);
    chain.unshift(video);  // Add to beginning

    // Check if this is a remix
    currentId = video['x-remixed-from'] || null;
  }

  return chain;
}

const chain = await getRemixChain('video_xyz789');

console.log('Remix chain:');
for (let i = 0; i < chain.length; i++) {
  console.log(`${i + 1}. ${chain[i].id} - "${chain[i].prompt}"`);
}

// Output:
// 1. video_abc123 - "Climate protest"
// 2. video_def456 - "Climate protest with large crowd"
// 3. video_xyz789 - "Powerful climate protest with thousands"
```

## Required Scopes

This endpoint requires the following API key scopes:

* `video:write` - Create videos (remixes)
* `video:read` - Access source video settings

See the [Authentication Guide](/guides/authentication#api-key-scopes) for more details.

## Next Steps

<CardGroup cols={2}>
  <Card title="Create Video" icon="plus" href="/api/videos/create">
    Create an original video to remix
  </Card>

  <Card title="Retrieve Video" icon="eye" href="/api/videos/retrieve">
    Check source video settings
  </Card>

  <Card title="Download Video" icon="download" href="/api/videos/download">
    Download completed remixes
  </Card>

  <Card title="Theory of Change" icon="compass" href="/guides/theory-of-change">
    Use Theory of Change in remixes
  </Card>
</CardGroup>
