# Higgsfield Genjutsu

> Один ролик, три режима: перенос движения, замена объекта и новый стиль. От 4 до 30 секунд, до 1080p.

Higgsfield Genjutsu берёт готовый ролик и меняет в нём реальность, не переснимая движение.

Перенос движения строит новую сцену по вашим фото — персонажа, место, стиль — и сохраняет движение, камеру и тайминг исходного видео.

Замена объекта меняет выбранного человека, одежду, товар или предмет. Остальной кадр остаётся как снят.

Новый стиль накладывает выбранный визуальный стиль и сохраняет движение и звук ролика. Фото можно не прикладывать: стиль применится к тем, кто уже в кадре.

Видео от 4 секунд, длиннее 30 секунд обрежется до 30. Разрешение 480p, 720p или 1080p. Описание можно не писать. Цена одна для всех режимов — за каждую секунду вашего ролика.

Одна модель `higgsfield-genjutsu` · ~3 мин. Режим — поле `input.mode`.

## Режимы

Один ID `higgsfield-genjutsu`. Что сделать с роликом, задаёт `input.mode`. Если поле не передать, это перенос движения. Те же официальные id тоже работают: `higgsfield/genjutsu/object-swap/v1.0` и `higgsfield/genjutsu/restyle/v1.0` сами выбирают режим, `mode` тогда можно не писать.

| `mode` | Что делает | Что передать |
|---|---|---|
| `motion-transfer` | Движение, камера и тайминг берутся из видео, внешность — с фото | `video_url` + `image_urls` (1–8) |
| `object-swap` | В ролике меняется человек, одежда или предмет. Остальное кадр старается сохранить | `video_url` + `image_urls` (1–8) |
| `restyle` | Тот же ролик в выбранном стиле. Движение и звук исходного видео остаются | `video_url` + `preset_id`. `image_urls` — по желанию, до 5 |

Общее для всех трёх: видео от 4 секунд, длиннее 30 секунд обрежется до 30, длина результата такая же. `prompt` можно не писать. `resolution`: `480p`, `720p` (по умолчанию) или `1080p`. Цена не зависит от режима.

Для замены объекта исходный кадр должен быть не меньше 409 600 пикселей (ширина × высота). Обычное HD подходит, мелкое превью — нет.

### Стили для restyle

Список стилей: `GET https://mixmedia.tech/api/v1/genjutsu/presets`. Ключ не нужен. В `preset_id` передаётся `id` пункта, не название и не картинка превью.

```json
{
  "code": 200,
  "msg": "success",
  "data": {
    "items": [
      {
        "id": "c2143317-f28d-4c3c-a0b8-39bd547e08a7",
        "name": "Cel-Shaded CG Anime",
        "preview_url": "https://…/style.webp"
      }
    ]
  }
}
```

Список меняется. Если сохранённый `preset_id` перестал приниматься, запросите список заново и возьмите актуальный id.



## Цена

| Разрешение | Цена за секунду вашего видео |
|---|---|
| 480p | $0.414 |
| 720p | $0.886 |
| 1080p | $2.122 |

- Цена = цена секунды × длина ролика, округляется вверх до $0.001. Списывается при создании задачи и полностью возвращается, если задача не удалась.
- Цена — за каждую секунду вашего видео (от 4 до 30 с). Ролик длиннее 30 с обрезается до 30, длина результата такая же. `prompt` можно не передавать.

## Лимиты медиа

| Тип | Ограничения |
|---|---|
| Фото | **1–8** для переноса движения и замены объекта; **0–5** для нового стиля. JPG / PNG / WebP; публичная https-ссылка или [files/upload](https://docs.mixmedia.tech/endpoints/upload) — до **30 МБ** на файл |
| Видео | одно, `video_url`; от **4 с** до **30 с** (длиннее обрезается до 30); MP4 / MOV → на сервере в MP4; загрузка ≤ **200 МБ** |

Фото — публичные https или upload; видео и звук — только через files/upload.


## Параметры (поле `input`)

| Параметр | Описание | Значения (по умолчанию — жирным) |
|---|---|---|
| `mode` | Режим: `motion-transfer` — перенос движения на фото, `object-swap` — замена человека или предмета, `restyle` — тот же ролик в выбранном стиле. Можно не передавать вместе с официальным id модели: `…/object-swap/v1.0` и `…/restyle/v1.0` сами выбирают режим | **`motion-transfer`** · `object-swap` · `restyle` |
| `prompt` | Что происходит в ролике: сцена, движение, камера, свет, стиль | текст, до 10000 символов (необязательно, по умолчанию пусто) |
| `video_url` | Видео с движением (MP4): одно, от 4 с. Длиннее 30 с обрезается до 30. Сначала загрузите через files/upload | `"https://…"` |
| `image_urls` | Фото персонажа (https): внешность, лицо, одежда | `["https://…"]`, до 8 |
| `preset_id` | Стиль для `restyle`: UUID из `GET /api/v1/genjutsu/presets`. Для других режимов не нужен | UUID стиля, обязательно при `mode: "restyle"` |
| `resolution` | Разрешение видео | `480p` · **`720p`** · `1080p` |

## Примеры

### Перенос движения

```bash
curl -X POST https://mixmedia.tech/api/v1/jobs/createTask \
  -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"model":"higgsfield-genjutsu","input":{"mode":"motion-transfer","prompt":"Сохрани лицо с фото, движение возьми из видео","video_url":"https://mixmedia.tech/files/<id>.mp4","image_urls":["https://mixmedia.tech/static/img/previews/gpt-t2i.jpg"],"resolution":"720p"}}'
```

```python
import os, requests

r = requests.post(
    "https://mixmedia.tech/api/v1/jobs/createTask",
    headers={"Authorization": f"Bearer {os.environ['API_KEY']}"},
    json={
      "model": "higgsfield-genjutsu",
      "input": {
        "mode": "motion-transfer",
        "prompt": "Сохрани лицо с фото, движение возьми из видео",
        "video_url": "https://mixmedia.tech/files/<id>.mp4",
        "image_urls": [
          "https://mixmedia.tech/static/img/previews/gpt-t2i.jpg"
        ],
        "resolution": "720p"
      }
    },
)
print(r.json())  # {"code": 200, "data": {"taskId": "..."}}
```

```javascript
const r = await fetch("https://mixmedia.tech/api/v1/jobs/createTask", {
  method: "POST",
  headers: { Authorization: `Bearer ${process.env.API_KEY}`, "Content-Type": "application/json" },
  body: JSON.stringify({
    "model": "higgsfield-genjutsu",
    "input": {
      "mode": "motion-transfer",
      "prompt": "Сохрани лицо с фото, движение возьми из видео",
      "video_url": "https://mixmedia.tech/files/<id>.mp4",
      "image_urls": [
        "https://mixmedia.tech/static/img/previews/gpt-t2i.jpg"
      ],
      "resolution": "720p"
    }
  }),
});
console.log(await r.json()); // { code: 200, data: { taskId: "..." } }
```

### Замена объекта

```bash
curl -X POST https://mixmedia.tech/api/v1/jobs/createTask \
  -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"model":"higgsfield-genjutsu","input":{"mode":"object-swap","prompt":"Замени человека на персонажа с фото","video_url":"https://mixmedia.tech/files/<id>.mp4","image_urls":["https://mixmedia.tech/static/img/previews/gpt-t2i.jpg"],"resolution":"720p"}}'
```

```python
import os, requests

r = requests.post(
    "https://mixmedia.tech/api/v1/jobs/createTask",
    headers={"Authorization": f"Bearer {os.environ['API_KEY']}"},
    json={
      "model": "higgsfield-genjutsu",
      "input": {
        "mode": "object-swap",
        "prompt": "Замени человека на персонажа с фото",
        "video_url": "https://mixmedia.tech/files/<id>.mp4",
        "image_urls": [
          "https://mixmedia.tech/static/img/previews/gpt-t2i.jpg"
        ],
        "resolution": "720p"
      }
    },
)
print(r.json())  # {"code": 200, "data": {"taskId": "..."}}
```

```javascript
const r = await fetch("https://mixmedia.tech/api/v1/jobs/createTask", {
  method: "POST",
  headers: { Authorization: `Bearer ${process.env.API_KEY}`, "Content-Type": "application/json" },
  body: JSON.stringify({
    "model": "higgsfield-genjutsu",
    "input": {
      "mode": "object-swap",
      "prompt": "Замени человека на персонажа с фото",
      "video_url": "https://mixmedia.tech/files/<id>.mp4",
      "image_urls": [
        "https://mixmedia.tech/static/img/previews/gpt-t2i.jpg"
      ],
      "resolution": "720p"
    }
  }),
});
console.log(await r.json()); // { code: 200, data: { taskId: "..." } }
```

### Новый стиль

```bash
curl -X POST https://mixmedia.tech/api/v1/jobs/createTask \
  -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"model":"higgsfield-genjutsu","input":{"mode":"restyle","video_url":"https://mixmedia.tech/files/<id>.mp4","preset_id":"<preset_id>","resolution":"720p"}}'
```

```python
import os, requests

r = requests.post(
    "https://mixmedia.tech/api/v1/jobs/createTask",
    headers={"Authorization": f"Bearer {os.environ['API_KEY']}"},
    json={
      "model": "higgsfield-genjutsu",
      "input": {
        "mode": "restyle",
        "video_url": "https://mixmedia.tech/files/<id>.mp4",
        "preset_id": "<preset_id>",
        "resolution": "720p"
      }
    },
)
print(r.json())  # {"code": 200, "data": {"taskId": "..."}}
```

```javascript
const r = await fetch("https://mixmedia.tech/api/v1/jobs/createTask", {
  method: "POST",
  headers: { Authorization: `Bearer ${process.env.API_KEY}`, "Content-Type": "application/json" },
  body: JSON.stringify({
    "model": "higgsfield-genjutsu",
    "input": {
      "mode": "restyle",
      "video_url": "https://mixmedia.tech/files/<id>.mp4",
      "preset_id": "<preset_id>",
      "resolution": "720p"
    }
  }),
});
console.log(await r.json()); // { code: 200, data: { taskId: "..." } }
```

## Создание и результат

1. `POST /api/v1/jobs/createTask` с `model` и `input`.
2. Поллите `GET /api/v1/jobs/recordInfo?taskId=…` каждые 3–5 с (или [callback](https://docs.mixmedia.tech/callbacks)).

### Успешный createTask (HTTP 200)

```json
{
  "code": 200,
  "msg": "success",
  "data": {
    "taskId": "task_...",
    "task_id": "task_...",
    "status": "submitted"
  }
}
```

### Готовая задача (recordInfo)

```json
{
  "code": 200,
  "msg": "success",
  "data": {
    "taskId": "task_...",
    "task_id": "task_...",
    "state": "success",
    "status": "completed",
    "costUsd": "0.384",
    "refunded": false,
    "resultJson": {
      "videos": [
        {
          "url": "https://…/out.mp4"
        }
      ],
      "duration": 8
    }
  }
}
```

Алиасы: `task_id` = `taskId`; `status`: `pending` / `processing` / `completed` / `failed` рядом с `state`. Ссылка на ролик — в `resultJson`. `size` — синоним `aspect_ratio` в `input`.


Фото можно передать любыми публичными https-ссылками или загрузить через [files/upload](https://docs.mixmedia.tech/endpoints/upload). Видео и звук — только через files/upload.

## Обработка ошибок

Ответ API — JSON `{"code", "msg", "data"}`; при ошибке есть строковое поле `error` (тип). **На `/api/v1/*` HTTP-статус совпадает с `code` в теле.**

### Примеры тел ответа

**400**

```json
{
  "code": 400,
  "msg": "input.duration: must be between 4 and 30, or \"auto\" / -1",
  "data": null,
  "error": "invalid_request_error"
}
```

**401**

```json
{
  "code": 401,
  "msg": "Authentication failed. Please check your API key",
  "data": null,
  "error": "authentication_error"
}
```

**402**

```json
{
  "code": 402,
  "msg": "Insufficient balance. Please top up and try again. Available: $0.01, required: $0.015. Top up at https://mixmedia.tech/cabinet/billing",
  "data": {
    "balance": "0.0100",
    "required": "0.0150",
    "currency": "USD",
    "topUpUrl": "https://mixmedia.tech/cabinet/billing"
  },
  "error": "payment_required"
}
```

**429**

```json
{
  "code": 429,
  "msg": "Rate limit exceeded: 10 createTask/upload requests per 10 s per key. Retry after 8s",
  "data": null,
  "error": "rate_limit_error"
}
```

**500**

```json
{
  "code": 500,
  "msg": "Internal server error",
  "data": null,
  "error": "server_error"
}
```

### Коды при createTask / upload

| code | error | Что значит | Что делать |
|---|---|---|---|
| 400 | `invalid_request_error` | Неверный параметр или тело | Смотрите `msg` |
| 400 | `unsupported_model` | Нет такой модели | [Список моделей](https://docs.mixmedia.tech/models) |
| 400 | `nsfw_content_detected` | Модерация / content policy — в `msg` / `failMsg` текст upstream как есть | Смотрите текст отказа, измените описание или файлы |
| 401 | `authentication_error` | Нет ключа или он неверный | Проверьте ключ |
| 402 | `payment_required` | Не хватает денег | Пополните баланс |
| 403 | `permission_error` | Нет доступа | Напишите в поддержку |
| 429 | `rate_limit_error` | Слишком много запросов | Подождите `Retry-After` |
| 505 | `model_disabled` | Модель временно отключена | Выберите другую |
| 500 | `server_error` | Сбой у нас | Повторите позже |

Примеры `msg`: `Authentication failed. Please check your API key` (401); `Insufficient balance. Please top up and try again…` (402).

### Ошибки задачи (recordInfo)

Если задача уже создана и завершилась неудачей: `state: "fail"` (алиас `status: "failed"`), `failCode` / `failMsg`, объект `error: { code, message, type }`. Деньги возвращаются (`refunded: true`).

| failCode | error.type | Что значит |
|---|---|---|
| 400 | `nsfw_content_detected` / `invalid_request_error` | Модерация — `failMsg` = текст upstream как есть |
| 422 | `invalid_request_error` | Модель не приняла параметры (длина, режим, разрешение, файл) |
| 501 | `server_error` | Модель не смогла сгенерировать (`failMsg`) |
| 504 | `timeout_error` | Слишком долго — повторите |
| 500 / 503 | `server_error` | Сервис временно недоступен |

### Лимиты запросов

| Что | Лимит |
|---|---|
| createTask и upload | 20 за 10 с на ключ |
| recordInfo / list / balance | 100 за 10 с на ключ |

Сверх лимита — HTTP 429 и `Retry-After`.
