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

# Wan 2.7 Video Editing

> Alibaba Wan wan2.7-videoedit instruction-based video editing

## Introduction

Call Alibaba Cloud Bailian `wan2.7-videoedit` through the Gravitex gateway to perform instruction-based editing on existing video (replace subjects, change clothing, adjust scene style, etc.). The API is async: submit a task first, then poll for results.

| Endpoint                  | Method | Path                              |
| ------------------------- | ------ | --------------------------------- |
| Create video editing task | `POST` | `/v1/video/generations`           |
| Query task status         | `GET`  | `/v1/video/generations/{task_id}` |

`{task_id}` is the `id` returned when the task is created.

<Note>
  Shares the unified video entry with [Wan 2.7 Video Generation](/en/api-reference/endpoint/wan2.7) (T2V / I2V / R2V), but the model and `media` parameter semantics differ—do not mix them.
</Note>

## Authentication

<ParamField header="Authorization" type="string" required>
  Bearer Token, e.g. `Bearer sk-xxxxxxxx`
</ParamField>

<ParamField header="Content-Type" type="string" required>
  Must be `application/json`
</ParamField>

<ParamField header="X-Trace-ID" type="string">
  Request trace ID; generate a unique value per request
</ParamField>

## Request parameters

<ParamField body="model" type="string" required>
  Must be `wan2.7-videoedit`
</ParamField>

<ParamField body="prompt" type="string" required>
  Video editing instruction
</ParamField>

<ParamField body="duration" type="integer">
  Output video duration in seconds; example uses `4`
</ParamField>

<ParamField body="metadata.input.prompt" type="string" required>
  Raw prompt passed to Alibaba Cloud; recommend keeping it in sync with top-level `prompt`
</ParamField>

<ParamField body="metadata.input.media" type="array" required>
  Input media array; must include at least one video
</ParamField>

<ParamField body="metadata.input.media[].type" type="string" required>
  Use `video` for source video; `reference_image` for reference images (up to 4)
</ParamField>

<ParamField body="metadata.input.media[].url" type="string" required>
  Public HTTP/HTTPS URL or temporary URL meeting upstream requirements
</ParamField>

<ParamField body="metadata.parameters.resolution" type="string">
  `720P` or `1080P`
</ParamField>

<ParamField body="metadata.parameters.prompt_extend" type="boolean">
  Enable intelligent prompt rewriting
</ParamField>

<ParamField body="metadata.parameters.watermark" type="boolean">
  Add watermark
</ParamField>

## Request examples

**Minimal request (video only):**

```bash theme={null}
curl --location 'https://api.gravitex.ai/v1/video/generations' \
  --header 'Authorization: Bearer sk-xxxxxxxx' \
  --header 'Content-Type: application/json' \
  --header 'X-Trace-ID: wan27-videoedit-test-001' \
  --data '{
    "model": "wan2.7-videoedit",
    "prompt": "背景不变，把视频中的小羊换成黑色的",
    "duration": 4,
    "metadata": {
      "input": {
        "prompt": "背景不变，把视频中的小羊换成黑色的",
        "media": [
          {
            "type": "video",
            "url": "https://your-public-oss.example.com/input.mp4"
          }
        ]
      },
      "parameters": {
        "resolution": "720P",
        "prompt_extend": true,
        "watermark": false
      }
    }
  }'
```

**With reference images:**

You can append reference images to the same `media` array; use "图 1" in the prompt to refer to them. Up to **4** reference images are supported.

```json theme={null}
{
  "model": "wan2.7-videoedit",
  "prompt": "保持视频中的人物动作不变，把人物衣服替换成参考图中的黑色西装",
  "duration": 4,
  "metadata": {
    "input": {
      "prompt": "保持视频中的人物动作不变，把人物衣服替换成参考图中的黑色西装",
      "media": [
        {
          "type": "video",
          "url": "https://your-public-oss.example.com/input.mp4"
        },
        {
          "type": "reference_image",
          "url": "https://your-public-oss.example.com/reference.png"
        }
      ]
    },
    "parameters": {
      "resolution": "720P",
      "prompt_extend": true,
      "watermark": false
    }
  }
}
```

## Create task response

On success, returns a standardized video task object (no outer `code/message/data` wrapper):

```json theme={null}
{
  "id": "76dc2556-f4****************29247",
  "object": "video",
  "model": "wan2.7-videoedit",
  "status": "queued",
  "progress": 0,
  "created_at": 1784714517,
  "metadata": {
    "output": {
      "task_id": "76dc2556-f4****************29247",
      "task_status": "PENDING"
    },
    "request_id": "86c168af-****************bf96d"
  }
}
```

Save the returned `id` for subsequent queries.

## Query task

```bash theme={null}
curl --location \
  'https://api.gravitex.ai/v1/video/generations/76dc2556-f4****************29247' \
  --header 'Authorization: Bearer sk-xxxxxxxx' \
  --header 'X-Trace-ID: wan27-videoedit-query-001'
```

Query response includes the outer `code/message/data` wrapper:

```json theme={null}
{
  "code": "success",
  "message": "",
  "data": {
    "id": "76dc2556-f4****************29247",
    "object": "video",
    "model": "wan2.7-videoedit",
    "status": "completed",
    "progress": 100,
    "created_at": 1784714517,
    "completed_at": 1784714687,
    "seconds": "8",
    "url": "https://signed-result-url.example.com/result.mp4",
    "video_url": "https://signed-result-url.example.com/result.mp4",
    "metadata": {
      "output": {
        "task_status": "SUCCEEDED",
        "input_video_duration": 4,
        "output_video_duration": 4,
        "video_url": "https://signed-result-url.example.com/result.mp4"
      }
    }
  }
}
```

## Status handling

Clients should use `data.status` as the platform standard status:

| `data.status` | Description | Client action                       |
| ------------- | ----------- | ----------------------------------- |
| `queued`      | Queued      | Continue polling                    |
| `in_progress` | Generating  | Continue polling                    |
| `completed`   | Success     | Read `data.video_url` or `data.url` |
| `failed`      | Failed      | Read `data.error.message`           |

Upstream status is in `data.metadata.output.task_status` (e.g. `PENDING`, `SUCCEEDED`); use for troubleshooting only, not as the primary status source.

Recommended poll interval: **5–15 seconds**; avoid high-frequency polling.

## Output video URL

On success, read `data.video_url` first; `data.url` is also available (typically the same in the current adapter).

Output URLs are signed temporary addresses—do not store them permanently as business resource URLs. Download or copy to your own OSS within the validity period for long-term storage.

## Duration and billing

In testing with `duration: 4`, upstream `usage` may look like:

| Field                   | Meaning                                                 |
| ----------------------- | ------------------------------------------------------- |
| `input_video_duration`  | Input video duration                                    |
| `output_video_duration` | Output video duration                                   |
| `usage.duration`        | Total billed duration (input + output)                  |
| `data.seconds`          | Gateway response total upstream duration (example: `8`) |

Therefore `data.seconds = 8` **does not mean** the output is 8 seconds—it is the combined input + output billing duration.

Alibaba Cloud official billing: charged by "input video duration + output video duration"; input images are not billed. See [official billing documentation](https://www.alibabacloud.com/help/en/model-studio/wan-video-editing-guide).

## Common error troubleshooting

| Issue                         | Description                                                                                                                              |
| ----------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------- |
| `media` missing video         | Must include at least `{ "type": "video", "url": "..." }`                                                                                |
| Video URL inaccessible        | Do not use login-required pages, insufficient-permission, or expired OSS URLs; must be publicly accessible                               |
| Invalid JSON                  | Use `true`/`false` for booleans, not `**true**` etc.                                                                                     |
| Only passed `video_url`       | New integrations should use `input.media`; the gateway accepts legacy `video_url` but it is not recommended                              |
| Mixed with Wan 2.1 parameters | `wan2.7-videoedit` uses `input.media`; legacy `wan2.1-vace-plus` uses `input.function = video_repainting` + `input.video_url`—do not mix |

## Minimal Python example

```python theme={null}
import time
import uuid
import requests

BASE_URL = "https://api.gravitex.ai"
API_KEY = "sk-xxxxxxxx"

headers = {
    "Authorization": f"Bearer {API_KEY}",
    "Content-Type": "application/json",
    "X-Trace-ID": uuid.uuid4().hex,
}

payload = {
    "model": "wan2.7-videoedit",
    "prompt": "背景不变，把视频中的小羊换成黑色的",
    "duration": 4,
    "metadata": {
        "input": {
            "prompt": "背景不变，把视频中的小羊换成黑色的",
            "media": [
                {
                    "type": "video",
                    "url": "https://your-public-oss.example.com/input.mp4",
                }
            ],
        },
        "parameters": {
            "resolution": "720P",
            "prompt_extend": True,
            "watermark": False,
        },
    },
}

response = requests.post(
    f"{BASE_URL}/v1/video/generations",
    headers=headers,
    json=payload,
    timeout=30,
)
response.raise_for_status()
task = response.json()
task_id = task["id"]

while True:
    result = requests.get(
        f"{BASE_URL}/v1/video/generations/{task_id}",
        headers={
            "Authorization": f"Bearer {API_KEY}",
            "X-Trace-ID": uuid.uuid4().hex,
        },
        timeout=30,
    )
    result.raise_for_status()
    data = result.json()["data"]
    status = data["status"]
    print(status, data.get("progress", 0))

    if status == "completed":
        print("video_url:", data.get("video_url") or data.get("url"))
        break
    if status == "failed":
        raise RuntimeError(data.get("error", {}).get("message", "video generation failed"))

    time.sleep(10)
```
