---
title: Live Commerce API
description: Call mobidoo/live-commerce — Intro/Mid/Fin scripts, per-clip delivery, and thread-based clip retry.
---

`mobidoo/live-commerce` is the Format Mobidoo uses for host-led live commerce
video. You get back a full cut **and** the individual clips that make it up.
When one clip is wrong, you continue the same **thread** and ask for that clip
only — not a new production from scratch.

**A product URL is the only field the Format needs.** Send a host image, a
tagged Korean script, and your own prices when you have them and they win; send
none of it and the Format derives each one from the product page — a host
generated to the product's tone, a script written from the page, and a Korean
voice preset.

Use a **Mobidoo team workspace** API key (created under the Mobidoo team, not a
personal key). A personal key calling `mobidoo/live-commerce` fails with
`403 workspace_key_required` — see
[Team Formats need a team key](/formats/call#team-formats-need-a-team-key).

Not in the Mobidoo workspace yet? Request an invite on Slack to
**허채원 (Chase Huh)**, or email [chase@sume.com](mailto:chase@sume.com) — see
[Workspace access](/enterprise/mobidoo#workspace-access).

## Create a run

Fill in the fields to rewrite the cURL, TypeScript, JavaScript, and Python
snippets.

<!-- api-call-example:format-mobidoo-live-commerce -->

The call returns a Format run. Poll
[`status_url` / `result_url`](/formats/runs), take a
[run webhook](/agents/run-webhooks) (`format.run.terminal`), or use
[`subscribeFormatRun`](/sdk/runs). Read
[Errors and rate limits](/workflows/errors-and-credits) before you put it
behind user traffic.

## What to send

| Field | Notes |
| --- | --- |
| `instruction` | Short prose: tone, “use Intro/Mid/Fin as written”, no BGM / no captions, vertical 9:16. |
| `input` | Structured caller data — product, host, price, tagged script. |
| `output_schema` | Bind `mobidoo/live-commerce/v1` so the receipt is typed for your ingest. |
| `primary_output_key` | `"full_video"` — the assembled cut is the primary media field. |
| `generation_spend_cap_usd` | Per-run generation ceiling (clamped to the Format’s own cap). |

There is no separate “JSON mode.” `instruction` is guidance; `input` is data.
Same shape as an LLM turn — see [Best practices](/enterprise/mobidoo/best-practices)
for one worked create → receipt → clip-retry loop.

### `input` keys the recipe recognises

`input` is free-form JSON concatenated into the prompt — not a fixed request
schema. Shape it however is convenient for your backend. Full rules:
[Calling a Format — what is carried](/formats/call#what-is-accepted-and-what-is-carried)
and [Structured output](/formats/structured-output).

None of the keys below are hard-required by HTTP. Extra keys ride along as data;
missing ones are not an error.

| Key | Effect |
| --- | --- |
| `product_url` | Page to crawl for specs, copy, and product photos. The one field worth treating as required. |
| `host_image_url` | Person to use as the host (identity for every talk shot). Alias: `avatar_image_url`. Omit and a host is generated to match the product's tone. |
| `script.segments` | Your script as **few long tagged blocks** — typically `Intro` / `Mid` / `Fin`. Do not pre-split into dozens of cuts. Omit and the Format writes one from the page. |
| `vo_language` | Spoken language. Defaults to Korean. |
| `on_card_name` | Exact product-name spelling allowed on an on-screen card. |
| `price` | List / sale / discount label when you want those numbers on a card. |
| `banner_reference_image_urls` | Layout references for on-screen cards (`media.sume.com` URLs). |
| `variants_per_n` | Candidate images per card. Defaults to 1. |

The delivered video is exactly as long as its voice track. To change length,
change the script — there is no duration parameter.

### Script tags → clip arrays

| Your tag | Receipt array |
| --- | --- |
| `Intro` | `opening[]` |
| `Mid` (or `Mid · …`) | `middle[]` |
| `Fin` | `closing[]` |

Send **few, long** segments (about 3–5), not one object per cut. The Format
decides where cuts fall; pre-splitting into ~45 cut objects wastes the
`input` size budget and usually makes a worse edit. Details under
[Size limits](#size-two-limits-and-only-one-of-them-tells-you) below.

## Threads: continue the same production

A Format run is one **agent turn**. The first successful run starts a
conversation. The receipt carries:

| Field | Meaning |
| --- | --- |
| `id` | This turn’s run id (`arun_…`). Poll / webhook this id. |
| `thread_id` | Durable conversation id for the whole production. **Response-only** — never put it in a request body; the API rejects it with `unknown_parameter`. |
| `previous_run_id` | Echoed when this turn continued an earlier run; otherwise `null`. |

To revise, create a **new** run on the same Format and pass
`previous_run_id` pointing at the finished turn. That is the partner equivalent
of “send another message on the same thread” (like an LLM
`previous_response_id`). Platform detail:
[Continue a run](/formats/runs#continue-a-run).

```text
Turn 1  POST …/runs          → arun_A, thread_id=thr_…
        output: full_video + opening[] + middle[] + closing[]

Turn 2  POST …/runs
        previous_run_id=arun_A
        “retry clip sc_7 only”
                             → arun_B, same thread_id=thr_…
        output: full scene list again (sc_7 updated; others unchanged)
```

Important habits:

- **Continue with `previous_run_id`, never by posting `thread_id`.** A body
  carrying `thread_id` is rejected with `unknown_parameter` — it is a response
  field on both turns, not an input.
- **A continuation is a new run** — new id, its own spend, its own single
  terminal webhook. The old run stays `completed` forever.
- **Send the same `output_schema` every turn.** Binding is per-run, not
  inherited.
- Group turns in your DB by the shared `thread_id`.

## Get the clips you asked for

Bind the partner schema (`mobidoo/live-commerce/v1`) and you receive:

- `full_video` — assembled vertical cut
- `opening[]` / `middle[]` / `closing[]` — **each scene is its own file**
  (`scene.video`), with a stable `scene.id`

So you can publish the full cut **or** pick individual clips for your own
editor / QA. Scene ids are **frozen for the life of that production** — store
them; do not key off array index.

Treat `scene.id` as an **opaque string**. Current productions return
`sc_0`, `sc_1`, … but earlier ones returned other shapes, so validating or
constructing ids against a pattern will break. Store whatever `scene.id` the
receipt hands you and send that back on the retry.

A realistic first-turn body (Samsung Q9000 short sample — swap URL / copy for
your catalog):

```json
{
  "instruction": "제공된 Intro/Mid/Fin 대본을 축약하지 말고 그대로 사용. 한국어 쇼호스트, 세로 9:16, 무BGM, 무자막. 카드 표기는 Q9000.",
  "input": {
    "product_url": "https://item.gmarket.co.kr/Item?goodscode=4376824000",
    "host_image_url": "https://media.sume.com/assets/asset_707ef7a4149340d494a12278ef4e95fe/source.png",
    "vo_language": "ko",
    "on_card_name": "삼성 AI Q9000 멀티",
    "price": {
      "list": "2,010,280원",
      "sale": "1,759,000원",
      "discount_label": "12%"
    },
    "script": {
      "segments": [
        {
          "tag": "Intro",
          "text": "안녕하세요, AI 쇼호스트 유라입니다. 오늘은 삼성 AI Q9000 멀티를 짧게 소개해 드릴게요."
        },
        {
          "tag": "Mid",
          "text": "거실용과 침실용 두 대 구성이고, AI 쾌적과 무풍 냉방을 지원합니다. 정상가 이백일만 이백팔십원에서 지금 백칠십오만 구천원, 십이 퍼센트 할인입니다."
        },
        {
          "tag": "Fin",
          "text": "전국 기본 설치가 포함되어 있어요. 삼성 AI Q9000, 화면에서 확인해 보세요. 감사합니다."
        }
      ]
    }
  },
  "output_schema": { "...": "mobidoo/live-commerce/v1 — see Best practices" },
  "primary_output_key": "full_video",
  "generation_spend_cap_usd": 45
}
```

A filled Q9000 call, the receipt it returns, and the clip-retry turn:
[Best practices](/enterprise/mobidoo/best-practices).

## Retry only the clips you want

Built for a dashboard “retry this clip” control: persist create `run.id` + each
receipt `scene.id`, then continue the thread with a fixed instruction and the
selected id(s). Do **not** start a fresh run with the same script unless you
want a new production (new ids, full cost).

When the operator is rejecting a take rather than repairing a `stand-in`, put
their note in `instruction` — which scene, what is wrong, how it should change.
See
[Prefer concrete per-scene feedback](/enterprise/mobidoo/best-practices#prefer-concrete-per-scene-feedback).

### Example — one Mid talk clip

```json
{
  "previous_run_id": "arun_42ed6a48b3d44092",
  "instruction": "Retry the selected scene only. Keep every other scene and the VO spine unchanged. Do not change the script.",
  "input": {
    "scene_id": "sc_7"
  },
  "output_schema": { "...": "identical to turn 1" },
  "primary_output_key": "full_video",
  "generation_spend_cap_usd": 8
}
```

### Example — two clips that came back stand-in / failed

```json
{
  "previous_run_id": "arun_42ed6a48b3d44092",
  "instruction": "Retry the selected scenes only. Keep every other scene and the VO spine unchanged.",
  "input": {
    "scene_ids": ["sc_7", "sc_9"]
  },
  "output_schema": { "...": "identical to turn 1" },
  "primary_output_key": "full_video",
  "generation_spend_cap_usd": 12
}
```

Neither body carries `thread_id`. `previous_run_id` is the whole continuation
contract.

The receipt is again a **full** `opening` / `middle` / `closing` list — not a
delta and not a patch. Regenerated scenes get new media URLs; every other scene
keeps the URL from the previous turn. Same `thread_id` on both runs, and
`full_video` comes back re-assembled with a new URL of its own.

Measured on the production pair in
[Best practices](/enterprise/mobidoo/best-practices) — one `sc_7` retry on an
11-scene cut: all 11 rows returned, **10 of 11 sibling URLs byte-identical**,
only `sc_7` moved, `full_video` re-assembled at the same 73.360 s. The scene's
previous URL still resolves `200` afterwards, so a create receipt you already
persisted stays valid and you can keep both takes.

### What clip retry can and cannot change

| Ask | Answer |
| --- | --- |
| Different take, framing, lighting, or B-roll for named scene ids | ✅ re-render those clips + re-assemble |
| Finalize a scene that came back `stand-in` or `failed` | ✅ same |
| Different wording, price read, or longer/shorter line | ❌ new full production (voice spine moves) |
| Different host, script, or product | ❌ new full production |

The line is the voice track. Looks change → clip retry. Words change → new
production and new scene ids.

**A retry is a new generation, not a re-encode.** Everything generative in the
frame is re-rolled: structured overlay facts (price, discount label, spec
badges, set composition) held exactly on the measured pair, but decorative
copy rendered inside generated product art changed wording between takes. Tell
operators they are getting **another take**, not the same frame with one detail
corrected.

### Budgeting a retry

Budget **about a twentieth of the create run** per single-clip retry when
script, VO, host stills, and every other clip are reused. Measured on
production: **$0.742530** against a **$14.959638** create — one `under_banner`
scene on an 11-scene cut, 2m53s wall.

It is a budgeting guide, not a guarantee. A short production spreads its fixed
work over fewer clips, and an earlier 8-scene cut measured closer to a fifth.
Size a dashboard budget from your own first runs and keep
`generation_spend_cap_usd` on every retry.

## Size: two limits, and only one of them tells you

Worth knowing before you send a full broadcast script — the two limits fail in
opposite ways.

- **`input` is rejected above 8 192 UTF-8 bytes** (and above 64 top-level
  properties), measured on the compact JSON → loud `400`.
- **Each field is then carried into the run cut at ~4 000 characters**, keeping
  the beginning → **no error**; the tail is simply missing.

So a script of about **six minutes or less arrives whole**. Past roughly
thirteen minutes you get a clean `400`. In between, the call can succeed while
the end of your script never reaches the run.

**For a 5–15 minute broadcast, run one call per product block** of 1–3 minutes
and merge on your side — then retry only the weak block’s thread.

`input` must be a JSON **object** (a stringified blob is `400`). `attachments`
carries images only (up to 30), so a script cannot be sent as a file.

## Partner FAQ: webhooks

Direct answers to the usual integration questions:

| Question | Answer for Formats API |
| --- | --- |
| **(A)** Does a webhook fire **per scene** as each clip finishes? | **No** for the Format run webhook. Formats does not emit a Formats response per scene. |
| **(B)** Does one webhook fire when **all scenes** for the turn are done? | **Yes.** When the agent turn completes, you get the final structured receipt and one `format.run.terminal` webhook. |

Formats calls an AI agent. Tool calls (TTS, clip renders, assemble) may finish
at different times underneath, but the **partner contract** is turn-final: one
receipt, one Format run webhook. A scene-regen continuation is a **new** run and
fires its own single terminal webhook.

Job-layer events (per media job) exist for internal progress and are **not** the
published partner scene webhook yet — ask us if you need that mapping.

More detail: [Run webhooks](/agents/run-webhooks).

## Webhooks: two layers

| Layer | Unit | Fires |
| --- | --- | --- |
| Format run | one agent turn | **once**, `format.run.terminal`, when the turn ends |
| Generation job | one media job | per job, as each completes |

Build on the Format layer. A clip retry fires exactly one new terminal webhook
against the **new** run id.

## Partner FAQ: scene regeneration

| Question | Answer |
| --- | --- |
| Can I regenerate **one scene** inside a finished production? | **Yes** — continue the same thread with `previous_run_id` and name the scene id(s). See [Retry only the clips you want](#retry-only-the-clips-you-want). |
| Do I have to re-run the **whole** video? | Only when wording / host / product / VO spine must change. Looks-only fixes use clip retry. |
| How do I continue the conversation? | Send `previous_run_id` (not `thread_id`). Both runs share `thread_id` on the receipt. |

Platform contract: [Continue a run](/formats/runs#continue-a-run).

## Shareable links

Copy-paste for Slack / email.

| Topic | Link |
| --- | --- |
| Webhook FAQ (A vs B) | https://docs.sume.com/enterprise/mobidoo/live-commerce#partner-faq-webhooks |
| Scene regeneration FAQ | https://docs.sume.com/enterprise/mobidoo/live-commerce#partner-faq-scene-regeneration |
| Threads / continue | https://docs.sume.com/enterprise/mobidoo/live-commerce#threads-continue-the-same-production |
| Clip retry recipes | https://docs.sume.com/enterprise/mobidoo/live-commerce#retry-only-the-clips-you-want |
| Best practices + examples | https://docs.sume.com/enterprise/mobidoo/best-practices |
| KO: webhook FAQ | https://docs.sume.com/ko-KR/enterprise/mobidoo/live-commerce#파트너-faq-웹훅 |
| KO: scene regen FAQ | https://docs.sume.com/ko-KR/enterprise/mobidoo/live-commerce#파트너-faq-씬-재생성 |

## Next

- [Best practices](/enterprise/mobidoo/best-practices) — create curl, receipt,
  clip-retry curl.
- [Continue a run](/formats/runs#continue-a-run) — platform thread contract.
- [Structured output](/formats/structured-output) — schema rules.
- [Quick start](/quick-start) — first generation end to end.
