# Run a system

A system makes a whole video for you, start to finish: a Drama Ad, a Street Interview, a Product Commercial and [the rest](https://vidlab.ai/vidlab-api/systems). Name a system or just describe the video; the run plans and makes it; you approve the quote and the checkpoints. You call it from your code or your own agent with one key.

## How a run works

1. `POST /v1/system-runs` with what you want made (`prompt`), and optionally the `system`. You get a `run_id` at once, and the run is `running`.
2. Read `GET /v1/system-runs/{run_id}` about every 10 seconds. `message` is what the run says now; `messages` is the whole conversation.
3. When the run needs you, `waiting_for` says what for:
   - `answers` (status `needs_input`): read `questions[]` (some have `options`) and answer them all in one reply: `POST /v1/system-runs/{run_id}/messages` with `{"text": "..."}`. With no questions, the run paused: say go on with `POST /v1/system-runs/{run_id}/approve`.
   - `quote` (status `needs_approval`): the next paid step costs about `quote_usd`. Say yes with `POST /v1/system-runs/{run_id}/approve`, or ask for a change with a reply.
   - `checkpoint` (status `needs_approval`): a script, frames or a first clip waits in `checkpoints[]`. Approve it (`POST /v1/system-runs/{run_id}/approve`, optionally with its `checkpoint_id`), or reply with a change.
4. When the run is `succeeded`, `output.videos[].url` holds the finished video (and `output.images`, `output.audio`; `output.text` holds a written answer, such as hooks or a script). Download it right away.

Send `"approval": "auto"` with `max_price_usd` and the run goes on by itself while its media spend stays within that cap; anything over it waits for your yes. You can still reply or cancel while it works.

```bash
curl -s https://vidlab.ai/v1/system-runs \
  -H "Authorization: Bearer $VIDLAB_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: coffee-ad-1" \
  -d '{"system": "drama-ad", "prompt": "Make a Drama Ad for my coffee brand: a woman in her kitchen, warm morning light", "inputs": {"images": ["https://example.com/bag.png"], "aspect_ratio": "9:16"}}'
```

## The systems

`GET /v1/system-runs/catalog` (no key) lists them: `id`, `name`, `description` (one line of what it is), `kind` (`video` or `image`) and a `thumbnail_url`. `available` is `false` while running systems is not switched on yet. A system is a way of making a video, not a product with a fixed length or price: what a run costs depends on what you ask, and each paid step is quoted before anything is spent. The answer may be cached for 10 minutes. The dashboard shows them on the [Systems page](https://vidlab.ai/vidlab-api/systems), where each has a "Copy API call".

## The request

| Field | Type | What it is |
| - | - | - |
| `prompt` | string, 3 to 8,000 characters | What you want made, in plain words. Required. |
| `system` | string | One of the systems (an `id` from `GET /v1/system-runs/catalog`). Name it in the prompt too. |
| `effort` | `medium`, `high` | How much care goes into each step. Default `medium`. |
| `approval` | `ask`, `auto` | `ask` (the default): it asks before each paid step and at each checkpoint. `auto`: it goes on by itself up to `max_price_usd`. |
| `max_price_usd` | number, up to 1,000 | The most the run may spend on media without asking. Required with `auto`. |
| `checkpoints` | `key`, `all`, `none` | Where it stops for a look: `key` (the default: the script and the first clip), `all` (frames too) or `none`. |
| `media` | `fast`, `balanced`, `quality` | How the media is made. Default `balanced`. |
| `brand_id` | string | One of your brands (the app's brand id). Default: your selected brand. The run uses what it knows about it. |
| `inputs` | object | Your files and words: `images` (up to 10 https links), `audio`, `video` (one https link each), `product_name`, `brief`, `script`, `aspect_ratio` (`9:16`, `16:9`, `1:1`, `4:5`). Upload a local file first (`POST /v1/uploads`). |

An `Idempotency-Key` header makes a resend find the same run for the rest of the UTC day.

## Endpoints

| Method | Path | What it does |
| - | - | - |
| `GET` | `/v1/system-runs/catalog` | The systems (public, no key; see above). |
| `POST` | `/v1/system-runs` | Starts a run. |
| `GET` | `/v1/system-runs` | Your runs, newest first (`?limit=` 1 to 100). |
| `GET` | `/v1/system-runs/{run_id}` | Reads a run. |
| `POST` | `/v1/system-runs/{run_id}/messages` | Replies to the run: `{"text": "..."}`, optionally with `inputs`. |
| `POST` | `/v1/system-runs/{run_id}/approve` | Says yes: to the next step's price, or past a checkpoint (`{"checkpoint_id": "..."}` optional). |
| `POST` | `/v1/system-runs/{run_id}/cancel` | Stops the run. Safe to repeat. |

Every call but the catalogue needs your key. Dashboard keys have full access; a scoped key needs the `runs` scope.

## The run

| Field | What it holds |
| - | - |
| `status` | `queued`, `running`, `needs_input`, `needs_approval`, `succeeded`, `failed` or `cancelled`. |
| `waiting_for` | `answers`, `quote`, `checkpoint`, or `null` while it works. |
| `message`, `messages[]` | What the run says now, and the conversation: `role` (`system` or `member`), `text`, `at`. |
| `questions[]` | While it asks: `id`, `question`, `options[]`. |
| `quote_usd` | While a paid step waits for your yes: about what it costs. |
| `checkpoints[]` | `id`, `kind` (`script`, `frames`, `clip`), `title`, `text`, `media[]` (`kind`, `url`) and `status` (`waiting`, `approved`, `changed`). |
| `warnings[]` | Short notes from the run. |
| `output` | Once `succeeded`: `videos[].url`, `images[].url`, `audio[].url`, `text`, `media_cost_usd`. |
| `price` | `run_usd` (the run's own planning and writing so far), `media_usd` (the media, once finished) and `held_usd` (set aside while it runs). |
| `error` | Once `failed` or stopped: `code` and `message`. |
| `next_action` | What to do now, in one sentence. |

## What it costs

A run is billed from your balance, the same balance as your generations:

- **Each media step** (an image, a clip, a voice-over) is an ordinary generation at that model's price, never above the price you approved. Each one shows in your usage logs.
- **The run's own planning and writing** is billed as it goes and shows in your usage logs under the system's name. `price.run_usd` is the total so far.
- When a run starts, a small amount (50 credits) is set aside, and it is released when the run ends.

A run stops if your balance runs out (`error.code` `api_wallet_exhausted`): what it made stays on the run. A cancel stops the billing at once. A run still going after 72 hours is stopped.

## Errors

| Code | HTTP | What to do |
| - | - | - |
| `invalid_request` | 400 | Fix the field the message names. |
| `unknown_field` | 400 | Send only the fields above. |
| `brand_not_found` | 404 | `brand_id` is not one of your active brands. |
| `api_wallet_exhausted` | 429 | Add credit, then start again. |
| `system_runs_rate_limited` | 429 | Too many new runs and replies for now: wait `Retry-After` seconds. |
| `system_run_not_found` | 404 | A run is visible only to the account that started it. |
| `system_run_ended` | 409 | The run is over: start a new one. |
| `system_run_conflict` | 409 | The run could not take that now (it is working, or nothing waits for your yes). Read the run and try again. |
| `not_available` | 404 | Running systems is not switched on yet. Meanwhile, run a model (`POST /v1/generations`). |
| `system_runs_unavailable` | 503 | Running systems is not available right now. Try again later, or run a model yourself (`POST /v1/generations`). |
