# Create personas and swap the person in the video

> Create a realistic person from traits, face photos or a ready-made character, and put them in any video or product photo.

A **persona** is a realistic person made from traits (age, hair, outfit), from the face photos of whoever uses it or from a ready-made character. Once saved, it replaces the person in a library video and keeps the scene, the camera, the movement and the sound. It also appears in photos holding a product.

Photo and video run in the background: you create and poll. `GET /personas` returns, per persona, `image_status` (`generating`, `ready` or `failed`) and `video_generating`, so a single call shows everything still in progress.

> Personas always charge, even on unlimited usage. Only creating from a ready-made character, restoring a version, reading a product from its link and rating are free. Read the prices from `GET /personas/catalog` and `GET /personas/swap/quote` on the spot, never hardcode them. If a generation fails, the credits come back on their own.

### Read the catalog

```bash
curl https://api.cut.pro/api/v1/personas/catalog \
  -H "X-Api-Key: $CUTPRO_API_KEY"
```

| Field | What it is for |
|---|---|
| `chip_options`, `default_chips` | All the options for each trait and the default. Build the form from here. |
| `face_photo_groups`, `max_face_photos` | The traits that still apply when the persona comes from face photos, and how many photos fit. |
| `video_styles` | The animation or illustration styles a video can receive. |
| `pricing` | Price of each photo and of each second of video per quality. |
| `video_limits` | Accepted duration, size and resolution of the source video, per quality. |

### Create the persona

Send one option per trait in `chips`. The photo starts generating and the response is `202`.

```bash
curl -X POST https://api.cut.pro/api/v1/personas \
  -H "X-Api-Key: $CUTPRO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "chips": {
      "vibe": "natural", "gender": "female", "age": "25_34", "origin": "latin",
      "skin": "medium", "eyes": "brown", "hair_style": "long_wavy", "hair_color": "brown",
      "facial_hair": "none", "face": "none", "body": "average", "outfit": "basic",
      "accessory": "none", "mark": "freckles"
    }
  }'
```

**With the face of whoever uses it.** Upload the photos first and pass the ids in `face_ids`, within 24 hours and up to `max_face_photos` (6). JPEG, PNG, WebP or HEIC up to 10 MB. `own_face_adult: true` confirms the photo is of whoever uses it and that this person is 18 or older.

```bash
curl -X POST https://api.cut.pro/api/v1/personas/faces \
  -H "X-Api-Key: $CUTPRO_API_KEY" \
  -F "photo=@selfie.jpg" \
  -F "own_face_adult=true"
```

With `face_ids`, only the traits in `face_photo_groups` still apply, and for those the `photo` option keeps the feature as it is in the photos.

**From a ready-made character, for free.** `GET /personas/presets` lists characters with the photo already made (`category`: `weird`, `cartoon`, `creator` or `influencers`). Creating from one generates nothing and is born with `image_status: ready`:

```bash
curl -X POST https://api.cut.pro/api/v1/personas/presets/$PRESET_ID \
  -H "X-Api-Key: $CUTPRO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "name": "Ana" }'
```

With `name`, the persona comes out already saved. Without `name`, it is a draft to save later. It remains that character: on a `regenerate`, the `chips` are ignored and only the `note` edits. `PUT /personas/presets/{presetId}/favorite` with `{ "favorite": true }` marks favorites.

### Adjust and save

Poll `GET /personas/{id}` every 5 seconds until `image_status` becomes `ready`, or `failed` with the reason in `error_code`.

To change the photo, generate again with other `chips` or edit with a one-line `note`, which keeps the same person:

```bash
curl -X POST https://api.cut.pro/api/v1/personas/$PERSONA_ID/regenerate \
  -H "X-Api-Key: $CUTPRO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "note": "make the jacket red" }'
```

An edit with `note` costs `pricing.photo_with_references_credits`. If another photo is already being generated, the response is `409 CARD_LOCKED`.

When the photo is good, save it. On a saved persona, the same call only renames it.

```bash
curl -X POST https://api.cut.pro/api/v1/personas/$PERSONA_ID/save \
  -H "X-Api-Key: $CUTPRO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "name": "Ana" }'
```

### Upload the source video

A video that is already in the library skips this step: use its id as `media_id`. For a new file, request the URL, upload and confirm. MP4 and MOV only, within `video_limits`.

```bash
curl -X POST https://api.cut.pro/api/v1/personas/sources/upload \
  -H "X-Api-Key: $CUTPRO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "file_name": "video.mp4", "file_size": 8421337, "content_type": "video/mp4" }'

curl -X PUT "$UPLOAD_URL" -H "Content-Type: video/mp4" --upload-file video.mp4

curl -X POST https://api.cut.pro/api/v1/personas/sources/upload/complete \
  -H "X-Api-Key: $CUTPRO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "media_id": "7412908365112823", "file_name": "video.mp4" }'
```

### Ask for the price

```bash
curl "https://api.cut.pro/api/v1/personas/swap/quote?media_id=7412908365112823&quality=standard&replace=head" \
  -H "X-Api-Key: $CUTPRO_API_KEY"
```

```json title="200 OK"
{ "media_id": "7412908365112823", "quality": "standard", "replace": "head", "style": null, "seconds": 8.4, "credits": 81 }
```

Nothing is charged. Show the price to the person and wait for a yes.

| Parameter | Values |
|---|---|
| `quality` | `economy` is `standard` at a lower resolution, upscaled to 1080p, and the cheapest. `standard` (default) follows the persona's traits. `high` also follows the photos, makes the face more faithful and accepts longer videos, for more. Prefer `high` on a persona made from face photos. |
| `replace` | `person` (default) swaps the whole person, with body and outfit. `head` swaps only the head and hair. `none` swaps nobody and only applies the `style`. |
| `style` | Redraws the whole video in a look, like `cel` (2D animation), `felt` (felt stop motion), `pixel` or `silent` (silent film), keeping camera and movement. With a style, the whole person is swapped, except with `replace: none`. Full list in `video_styles`. |

The price depends only on quality, duration and style. `head` and `person` cost the same. A video refused in `standard` may fit in `high`.

### Generate the video

Send the price's `credits`, with the same `quality`, `replace` and `style`. The response is `202` with `status: processing`.

```bash
curl -X POST https://api.cut.pro/api/v1/personas/$PERSONA_ID/videos \
  -H "X-Api-Key: $CUTPRO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "type": "swap", "media_id": "7412908365112823", "quality": "standard", "replace": "head", "credits": 81 }'
```

If the price changed, nothing is charged and the response is `409 PRICE_CHANGED` with the right value in `extra.credits`.

By default, whoever becomes the persona is the main person in the video. To choose another, send `target`:

- a person seen in the video: `{ "seconds": 2.5, "x": 0.62, "y": 0.48 }`, with the moment and their center from 0 to 1. `box` is optional and helps when there are people nearby.
- whoever is filming: `{ "camera": true }`.

Choosing the person never raises the confirmed price. If nobody is at that point, the video ends in `failed` with `TARGET_NOT_A_PERSON` and the credits come back.

Poll `GET /personas/{id}/videos/{videoId}` every 10 seconds.

- `ready`: `download_url` has the MP4, valid for 6 hours. To post or render, `POST /generations/{videoId}/edit` returns the `edit_id` that goes in [`POST /posts`](/en/publishing) or [`POST /renders`](/en/rendering).
- `failed`: the reason is in `error_code` and the credits have already come back.

## Versions and ratings

Each photo that finishes becomes a version and becomes the current one. The previous ones stay in `versions`, in `GET /personas/{id}`. Going back to one of them is free:

```bash
curl -X POST https://api.cut.pro/api/v1/personas/$PERSONA_ID/versions/$VERSION_ID/restore \
  -H "X-Api-Key: $CUTPRO_API_KEY"
```

Every video stores the version it came from in `version_id`. New videos use the current one, unless you send `version_id` in `POST /personas/{id}/videos`. `GET /personas/{id}/videos?version_id=...` filters by version. Videos already made do not change.

Rating is free and improves results:

```bash
curl -X POST https://api.cut.pro/api/v1/personas/$PERSONA_ID/versions/$VERSION_ID/feedback \
  -H "X-Api-Key: $CUTPRO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "vote": "down", "reason": "not_like_me" }'
```

| Rate | Route | Reasons for `down` |
|---|---|---|
| Photo | `POST /personas/{id}/versions/{versionId}/feedback` | `not_realistic`, `wrong_traits`, `not_like_me`, `defects`, `other` |
| Video | `POST /personas/{id}/videos/{videoId}/feedback` | `different_person`, `scene_changed`, `defects`, `bad_motion`, `other` |

`vote` is `up`, `down` or `null` (clears). A `down` on a video cancels its posts that are still scheduled.

`DELETE /personas/{id}` deletes the persona. The edits made with its videos remain.

## Photos with a product

A saved persona poses in a ready-made scene or one you write, holding a product. Start with the product.

**From the store link, for free.** Reads the name, description, price and brand, and stores up to 3 photos:

```bash
curl -X POST https://api.cut.pro/api/v1/personas/$PERSONA_ID/shots/product \
  -H "X-Api-Key: $CUTPRO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "url": "https://loja.exemplo.com.br/serum-vitamina-c" }'
```

```json title="200 OK"
{
  "product": { "name": "Vitamin C Serum 30 ml", "description": "Facial serum with 10% vitamin C.", "price": "R$ 89,90", "brand": "Example" },
  "photo_ids": ["7413100022003301", "7413100022003302"],
  "partial": false
}
```

It works on most stores, on Shopee and on Amazon Brazil. `partial: true` means the price or the photos did not come through. Up to 5 reads per minute; above that, `429 PRODUCT_RATE_LIMITED`.

**From the photo.** When the page does not open (`PRODUCT_NOT_READABLE`, always on Mercado Livre and TikTok Shop), upload the photos and write the fields. JPEG, PNG, WebP or HEIC up to 10 MB, with the label facing the camera. `rights_confirmed: true` says the person may use the product and the photo. Use the `id` within 24 hours.

```bash
curl -X POST https://api.cut.pro/api/v1/personas/$PERSONA_ID/shots/photos \
  -H "X-Api-Key: $CUTPRO_API_KEY" \
  -F "photo=@serum.jpg" \
  -F "rights_confirmed=true"
```

Generate the photo:

```bash
curl -X POST https://api.cut.pro/api/v1/personas/$PERSONA_ID/shots \
  -H "X-Api-Key: $CUTPRO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "template": "bathroom",
    "aspect_ratio": "9:16",
    "credits": 20,
    "product": { "name": "Vitamin C Serum 30 ml", "photo_ids": ["7413100022003301"] }
  }'
```

```json title="202 Accepted"
{ "id": "7413100030004401", "status": "processing", "credits_charged": 20 }
```

| Field | Values |
|---|---|
| `template` | `mirror` (default), `kitchen`, `bathroom`, `sofa`, `cafe`, `gym`, `street`, `closeup`, `story`, `bedroom`. The template decides what the persona does with the product. |
| `scene` | A written scene, in any language, from 3 to 500 characters. Replaces the `template`. |
| `product.action` | Only with `scene`: `show` (default, shows it to the camera) or `use` (uses it with one hand). A scene that requires both hands (applying, serving, opening, eating) is refused with `SCENE_ACTION_UNSUPPORTED` before charging. |
| `aspect_ratio` | `9:16` (default), `4:5`, `1:1`, `16:9`. |
| `credits` | The catalog's `pricing.scene_photo_credits`, double with `style`. A different price responds `409 PRICE_CHANGED`. |

Poll `GET /personas/{id}/shots/{shotId}` every 5 seconds. On `ready`, `image_url` has the photo. `GET /personas/{id}/shots` lists them all, from newest to oldest.

## Errors

| Code | When |
|---|---|
| `INSUFFICIENT_CREDITS` | Insufficient balance. `extra.credits_needed` says how much is missing, `extra.top_up_url` leads to the top-up, `extra.is_owner` says whether the caller can top up. |
| `PRICE_CHANGED` | The price changed. The new one is in `extra.credits`: show it and ask for a yes again. |
| `CARD_LOCKED` | A photo of the persona is already being generated. Wait for it to finish. |
| `CARD_NOT_READY` | Saved before the photo was `ready`. |
| `VIDEO_IN_PROGRESS` | The persona is already making the same video in the same version. |
| `VIDEO_DURATION_OUT_OF_RANGE`, `VIDEO_UNSUPPORTED` | The video falls outside `video_limits` at this `quality`. Try another. |
| `STYLE_REQUIRED` | `replace: none` without `style`. |
| `STYLE_UNSUPPORTED` | Style at `quality: high` on a video longer than `video_mid_max_seconds`. |
| `TARGET_UNSUPPORTED` | `target` after the end of the video, or on a `high` video longer than `video_mid_max_seconds`. |
| `INVALID_IMAGE` | Unreadable photo or outside the accepted formats. |
| `PRODUCT_URL_INVALID`, `PRODUCT_NOT_READABLE` | The link is not a product page, or it does not open. Upload the photos. |
| `PERSONA_NOT_FOUND`, `VERSION_NOT_FOUND`, `VIDEO_NOT_FOUND`, `PRESET_NOT_FOUND`, `FACE_NOT_FOUND`, `PHOTO_NOT_FOUND`, `SHOT_NOT_FOUND`, `MEDIA_NOT_FOUND` | The id does not exist in this workspace. |

When a photo or video ends in `failed`, `error_code` says why: `SAFETY_BLOCKED`, `COPYRIGHT_BLOCKED`, `REAL_FACES_BLOCKED` and `MEDIA_REJECTED` are refusals over the content (change the traits, the photos or the video); `MEDIA_FAILED`, `MEDIA_UNAVAILABLE` and `TIMEOUT` are worth another attempt. The credits have already come back in both cases.
