# Wandura C1.1: ad videos from a description

> **TL;DR**: `POST /v2/jobs` with `model: "wandura-c1-1"`, a `prompt` that describes the ad, and up to 4 reference images. You get back one finished video of 5 to 60 seconds. Wandura plans the shots, renders them, and joins them. You do not write a system prompt. The price is per output second: 480p $0.09, 720p $0.15, 1080p $0.30. Poll `GET /v2/jobs/{job_id}` or receive the signed webhook, exactly as for other video jobs. Failed, blocked and timed-out jobs are free.

## Basics
- **Model id**: `wandura-c1-1` ("Wandura C1.1"), output `video`. It is called through the [Jobs API](https://docs.wandura.ai/developers/api-v2/jobs) with the same keys, idempotency, webhooks and statuses as other jobs.
- **No `style`.** Send the description and the settings as top-level fields. A `style` or an `inputs` object is refused.
- **What it makes**: one continuous ad video. The video keeps the people, products and places in your reference images.

| Endpoint | What it does |
|---|---|
| `POST /v2/jobs` | Submit a C1.1 job. |
| `GET /v2/jobs/{job_id}` | Status and output. Same body as the webhook. |
| `POST /v2/jobs/{job_id}/cancel` | Cancel a job that is still `queued`. |
| `GET /v2/models/wandura-c1-1` | The model, with `price_per_second` by resolution. |
| `GET /v2/models/wandura-c1-1/schema` | JSON Schema of the submit body. |
| `GET /v2/public/workflows` | No key: C1.1 with its inputs and per-second prices. |

## Request
```bash
curl -X POST https://api.wandura.ai/v2/jobs \
  -H "Authorization: Bearer $WANDURA_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: ad-2026-10-20-001" \
  -d '{
    "model": "wandura-c1-1",
    "prompt": "A 15 second ad for Aurora, a meditation app. The woman in Image 1 sits by a window at sunrise, closes her eyes and breathes; the phone in Image 2 shows the app. Calm piano. She says: \"Ten minutes changed my mornings.\" End on the app logo.",
    "image_urls": ["https://your-bucket.example.com/ads/woman.jpg", "https://your-bucket.example.com/ads/phone.png"],
    "duration": 15,
    "resolution": "720p",
    "aspect_ratio": "9:16",
    "webhook_url": "https://your-backend.example.com/hooks/wandura",
    "end_user_id": "ua_member_17"
  }'
# HTTP 202
# {"job_id":"run_7Hq2LmPaX9Ws","status":"queued","model":"wandura-c1-1","created_at":"2026-10-20T02:14:46.251Z"}
```

| Field | Notes |
|---|---|
| `model` | Required. `wandura-c1-1`. |
| `prompt` | Required, 1 to 4000 characters. The ad: product, people, place, story, mood, and any spoken line or on-screen text in quotes. Refer to images as "Image 1", "Image 2" in the order you send them. |
| `image_urls` | Optional, 0 to 4 public https URLs (JPEG, PNG or WEBP; presigned URLs work). People, product, place. `[]` counts as none. |
| `duration` | Optional, whole seconds from 5 to 60. Default 5. The price is per second. |
| `resolution` | Optional, `480p`, `720p` or `1080p`. Default `720p`. The price per second depends on it. |
| `aspect_ratio` | Optional, `9:16`, `16:9` or `1:1`. Default `9:16`. |
| `lane` | Optional, `priority` (default) or `relax`. See [Relax lane](https://docs.wandura.ai/developers/api-v2/jobs#lane-and-inputs). |
| `num_outputs` | Optional, must be `1`. |
| `webhook_url` | Optional. A public https URL that gets the final job, signed. Needs a signing secret first (see [Webhooks](https://docs.wandura.ai/developers/api-v2/jobs#webhooks)). |
| `end_user_id` | Optional. Your id for the person the job is for. When you send it, the per-person daily job cap applies (see [Limits](https://docs.wandura.ai/developers/api-v2/wandura-c1-1#limits)). |

- **Input URLs are downloaded when the job runs**, not when you submit. Keep presigned URLs valid for at least 30 minutes.
- Send an `Idempotency-Key` header on every submit so a retry never makes a second video (see [Idempotency](https://docs.wandura.ai/developers/api-v2/jobs#idempotency)).

## Poll and output
```bash
curl https://api.wandura.ai/v2/jobs/run_7Hq2LmPaX9Ws -H "Authorization: Bearer $WANDURA_KEY"
```

```json
{"job_id":"run_7Hq2LmPaX9Ws","status":"succeeded","model":"wandura-c1-1","style":"sty_wandura_c1_1",
 "output":[{"url":"https://<wandura-storage-host>/outputs/2026-10-20/<random name>.mp4","type":"video/mp4",
   "width":720,"height":1280,"duration":15,"fps":30,"size_bytes":4183022,"seed":null,"expires_at":"2026-10-27T02:22:21.000Z"}],
 "error":null,"created_at":"2026-10-20T02:14:46.251Z","completed_at":"2026-10-20T02:22:21.964Z"}
```

- The body has the same fields as every other video job, so one parser handles all of them. For this model, `style` is a fixed value; ignore it.
- Statuses: `queued` → `processing` → `succeeded` | `failed` | `cancelled` | `timeout` (see [Statuses](https://docs.wandura.ai/developers/api-v2/jobs#statuses)).
- **How long it takes**: 2 to 8 minutes in our tests (5 to 30 s videos). Longer videos are rendered in parallel parts, so a 60 s video does not take 12 times as long as a 5 s one. Poll every 10 to 15 seconds, or use the webhook.
- **Output**: one MP4 on Wandura storage. The link works for at least 7 days (`expires_at`). Copy the file if you need it longer (see [Output](https://docs.wandura.ai/developers/api-v2/jobs#output)).

## Webhook
With `webhook_url`, Wandura POSTs the final job body (the same JSON as the poll) once the job ends, signed with `X-Signature` and `X-Timestamp`. Verification, retries and deduplication work exactly as for other jobs: see [Webhooks](https://docs.wandura.ai/developers/api-v2/jobs#webhooks) and [Verify the signature](https://docs.wandura.ai/developers/api-v2/jobs#webhook-verify).

## Prices
The price is per **output second**, by resolution. It covers the whole job: planning, rendering and joining.

| `resolution` | Per second | 5 s | 15 s | 30 s | 60 s |
|---|---|---|---|---|---|
| `480p` | $0.09 | $0.45 | $1.35 | $2.70 | $5.40 |
| `720p` (default) | $0.15 | $0.75 | $2.25 | $4.50 | $9.00 |
| `1080p` | $0.30 | $1.50 | $4.50 | $9.00 | $18.00 |

- When the job is accepted, the price for the `duration` you asked for is **held** on your wallet. When it succeeds, you are **charged for the seconds delivered**, never more than the hold.
- `failed`, `timeout` and `cancelled` jobs and refused submits (`4xx`) are **free**. The hold is released.
- The `relax` lane costs the same as `priority` for this model.
- Read the live prices from `GET /v2/models/wandura-c1-1` (`price_per_second`) or `GET /v2/public/workflows`.
- Test keys (`wdr_test_…`) are free: the job succeeds at once with a sample video and nothing is held.

## Limits
| What | Limit |
|---|---|
| Length | 5 to 60 s, whole seconds |
| Reference images | Up to 4 |
| Description | Up to 4000 characters |
| Outputs | One video per job |
| Maximum job time | 1080 s (18 minutes) from submit on the `priority` lane. A job that has not finished by then ends `timeout`, free. Submit it again. |
| Daily spend cap | Set per product by your org admin. Over it: `429 DAILY_CAP_REACHED` (see [Errors](https://docs.wandura.ai/developers/api-v2/wandura-c1-1#errors)). |
| Per end user | 20 jobs per day by default, only when you send `end_user_id` (`429 END_USER_LIMIT_REACHED`). |

When many C1.1 jobs run at once, new ones wait as `queued` until there is room. They are not refused.

## Errors
A bad body is refused at once with `422 INVALID_INPUT`, before any money is held. Every problem is listed:

```json
HTTP 422
{"error":{"code":"INVALID_INPUT","message":"Validation failed.","retryable":false,"details":[
  {"field":"image_urls","reason":"at most 4 reference images"},
  {"field":"duration","reason":"must be whole seconds from 5 to 60"},
  {"field":"style","reason":"wandura-c1-1 takes no style"}],
 "docs_url":"https://docs.wandura.ai/developers/api-v2/errors#jobs-invalid-input"}}
```

When your product has reached its daily spend cap, the submit is refused and nothing is held:

```json
HTTP 429
{"error":{"code":"DAILY_CAP_REACHED","message":"This product reached its daily spend cap (the day resets at 00:00 Vietnam time, UTC+7). Ask your org admin to raise the cap.","retryable":false,"docs_url":"https://docs.wandura.ai/developers/api-v2/errors#jobs-daily-cap-reached"}}
```

| HTTP | `code` | `retryable` | What to do |
|---|---|---|---|
| 422 | [`INVALID_INPUT`](https://docs.wandura.ai/developers/api-v2/errors#jobs-invalid-input) | false | Fix the fields listed in `details`. |
| 402 | [`INSUFFICIENT_CREDITS`](https://docs.wandura.ai/developers/api-v2/errors#jobs-insufficient-credits) | false | The wallet cannot cover the hold. Add funds. |
| 429 | [`DAILY_CAP_REACHED`](https://docs.wandura.ai/developers/api-v2/errors#jobs-daily-cap-reached) | false | Do not retry. Wait for 00:00 Vietnam time, or ask your org admin to raise the cap. |
| 429 | [`RATE_LIMITED`](https://docs.wandura.ai/developers/api-v2/errors#jobs-rate-limited), [`PRODUCT_BUSY`](https://docs.wandura.ai/developers/api-v2/errors#jobs-product-busy), [`END_USER_LIMIT_REACHED`](https://docs.wandura.ai/developers/api-v2/errors#jobs-end-user-limit-reached) | true | Retry after `Retry-After` with the same `Idempotency-Key`. |

A job that ends without a video is not an HTTP error. Its `error.code` is `GENERATION_FAILED` (retryable), `CONTENT_MODERATION_BLOCKED` (change the description or images) or `TIMEOUT` (submit again). All three are free. See [Statuses](https://docs.wandura.ai/developers/api-v2/jobs#statuses).

## Remake a source ad
Send an existing ad as `video_url` and get a new ad with the same scenes and pacing, with your people and your product.

```bash
curl -X POST https://api.wandura.ai/v2/jobs \
  -H "Authorization: Bearer $WANDURA_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: remake-2026-10-20-001" \
  -d '{
    "model": "wandura-c1-1",
    "video_url": "https://your-bucket.example.com/ads/source-ad.mp4",
    "prompt": "Remake it for Lumi sparkling water: a summer sale at a Lumi drinks store. The woman in Image 1 is the shop assistant.",
    "image_urls": ["https://your-bucket.example.com/ads/woman.jpg"],
    "resolution": "720p",
    "aspect_ratio": "16:9"
  }'
# HTTP 202
# {"job_id":"run_Pz81KqTmV2Rc","status":"queued","model":"wandura-c1-1","created_at":"2026-10-20T05:14:46.251Z"}
```

| Field | Notes |
|---|---|
| `video_url` | The source ad: an MP4 or MOV of at most 60 s at a public https URL. Presigned URLs and Wandura output URLs work. |
| `prompt` | Optional with `video_url`: what to change, such as your product, brand, message or language. Without it, the ad is remade as it is, with new people and a generic product. |
| `image_urls` | Optional, up to 4: the people, product and place to put in. |
| `duration` | Optional. Default: the source ad's length, from 5 to 60 s (a source ad shorter than 5 s gives 5 s). |

- **What stays**: the scenes in order, with their timing scaled to the new length, the shots and camera moves, the pacing, the music and sound, and the shape of the lines and on-screen text. **What changes**: the people (yours from the images, or new ones), the product and brand (yours from the prompt, or a generic one), and the lines, rewritten for your product. Real brands, logos and people from the source ad are not copied.
- **Read once**: Wandura reads a source ad once and reuses that reading for every later remake of the same video file, from any link or a re-upload. A Wandura C1.1 remake sent back as `video_url` reuses the reading of its original source ad. The reading only describes the source ad. It never holds your prompt, images or brand.
- **Price**: the per-second prices above. Reading the source ad costs nothing extra. The hold is for `duration`, so for the source ad's length when you do not send one.
- **Checked before any hold**: a `video_url` that is not a readable MP4 or MOV, or that is longer than 60 s, gets `422 INVALID_INPUT` and nothing is held. Keep the link valid for at least 30 minutes, because the file is downloaded again when the job runs.
- **Maximum job time**: 1080 s (18 minutes) from submit on the `priority` lane, the same as above.
- **Ends without a video, free**: a source ad with adult, violent, hateful or dangerous content ends `failed` with `CONTENT_MODERATION_BLOCKED`. A link that no longer serves a video of at most 60 s when the job runs ends with `INPUT_UNUSABLE`. A source host that is busy ends with `GENERATION_FAILED` (retryable).

```json
HTTP 422
{"error":{"code":"INVALID_INPUT","message":"Validation failed.","retryable":false,"details":[
  {"field":"video_url","reason":"the source ad lasts 75 s; at most 60 s"}],
 "docs_url":"https://docs.wandura.ai/developers/api-v2/errors#jobs-invalid-input"}}
```
