---
name: "Make UGC video"
repo: https://github.com/gitroomhq/agent-media
source: https://github.com/gitroomhq/agent-media/tree/HEAD
license: "Apache-2.0"
install: "npx skills add gitroomhq/agent-media --skill \"Agent-Media UGC Video\""
page: https://100xlab.com/resources/agent-skills/gitroomhq-agent-media--make-ugc--e26xea
---

# Agent-Media UGC Video

**One tool. One call. A finished, captioned, vertical UGC video.**

You give a `script` and (optionally) who says it — agent-media picks the pipeline, the take count, and the duration for you. You NEVER pick a sub-tool:

- **Short line** → one clean talking-head clip.
- **Full monologue** (any length) → a seamless multi-take video, **never trimmed to fit a clip**.
- **`broll_url`** → the person narrates over your b-roll / gameplay / product footage.

## Inputs

**What they say** — exactly one of:
- `script` — the spoken line, **any length**. A sentence becomes one clip; a paragraph becomes the full multi-take video. Never trimmed.
- `scene_action` — a silent clip instead (dancing, b-roll, vibes). Requires a `character`.

**Who says it** — optional, pass at most one (omit → a default person is generated):
- `person` — describe them in words, e.g. `"a 25-year-old woman with curly red hair"`.
- `image` — a photo of the person: a public `https` URL **or** base64. The face is locked to it.
- `character` — reuse a saved character: its `char_…` id (from `list_characters`) **or** its `character_sheet_url`.
- `name` — optional name/age/vibe hint, e.g. `"Sophia, 28"`.

> A **long monologue** or a **`broll_url`** review needs a real face — pass `image` or `character`, not just `person`.

**Captions are OPT-IN — do NOT add them on your own.** First ASK the user whether they want captions and which `caption_style` (`hormozi` | `tiktok` | `minimal`); set `captions:true` only if they say yes, otherwise leave it off.

**Look & format** — all optional:
- `captions` — off unless set true (ask first, above); `caption_style` (`hormozi` | `tiktok` | `minimal`)
- `look` (`natural` | `commercial` | `raw_iphone`), `aspect_ratio` (`9:16` | `1:1`)
- `broll_url` — an `https` video overlaid on the lower half while they narrate.
- `duration` — leave blank; length is inferred from the script. Set only to force a short clip.

## Examples

A quick clip from a text description:
```json
{ "script": "Honestly? This app saved my whole morning routine.", "person": "a friendly young woman, soft daylight" }
```

A full monologue from a saved character — multi-take, never trimmed:
```json
{ "script": "Okay I have to be honest with you for a second. Three months ago I was completely overwhelmed … (the entire monologue, as long as you like) … and that is your sign.", "character": "char_8f3ac210" }
```

From a photo of a real person:
```json
{ "script": "Wait — you have to see what this actually does.", "image": "https://example.com/face.jpg", "name": "Maya, 27" }
```

A narrated b-roll / gameplay review:
```json
{ "script": "Watch this play — this is where the whole match turns around.", "broll_url": "https://example.com/clip.mp4", "character": "char_8f3ac210" }
```

## Reuse the same person across a session

After a video finishes, the character is **saved** — `GET /v1/characters` lists it with a `character_id` (`char_…`) and a `character_sheet_url`. For the NEXT video in the same task, pass that as `character` instead of a new `person`: it keeps the exact same face, and it's faster + cheaper because it reuses the existing portrait + character sheet rather than re-making them (the character sheet is never skipped — it's reused). Only generate a NEW person when the user asks for a different one. **If you're not sure whether they want the same person or a new one, ASK the user before generating.**

## How to call it

Preferred path: MCP tool `mcp__agent-media__make_ugc`. The full schema is auto-published via `tools/list`; the fields above are the manual.

Fallback path: REST.
```http
POST https://api.agent-media.ai/v1/skills/make_ugc/run
Authorization: Bearer $AGENT_MEDIA_API_KEY
Content-Type: application/json
Idempotency-Key: <any unique string per intent>

{ "script": "Okay this completely changed how I work — I plan my whole week in ten minutes now.", "character": "char_8f3ac210" }
```

## Cost & timing

- Credits: `route-dependent: ~190–505 for a short clip; priced per-take for a long monologue or b-roll review`
- Wall time (typical): `360–1400s`
- Deducted as each take runs.

## Polling the result

```http
GET https://api.agent-media.ai/v1/skills/runs/<skill_run_id>
Authorization: Bearer $AGENT_MEDIA_API_KEY
```

Returns per-step `status` + `current_step`; `final_output.video_url` is your finished MP4 when `status` is `succeeded`.

**Keep the user posted — don't go silent.** A video takes a few minutes: a new person runs portrait → character sheet → the video (→ captions if asked); reusing a saved person skips straight to the video. When you submit, tell the user the plan + a rough ETA, then poll and report each `current_step` as it changes (e.g. "building the character sheet…", "rendering the video…", "adding captions…") so they always know what's happening.

## House rules

- See [reference/realism-rubric.md](../../reference/realism-rubric.md) for the realism doctrine baked into every prompt.
- See [reference/pacing.md](../../reference/pacing.md) — you don't manage pacing; make_ugc sizes every take to the words.
- See [reference/auth.md](../../reference/auth.md) for first-time install and `agent-media login`.

## Source of truth

Auto-generated by `scripts/generate-public-skill.ts` from `services/api-v2/src/skills/registry.ts`. Do not hand-edit; CI rejects drift.

---

## Supporting file: reference/auth.md

# Auth — first-time setup

agent-media uses a `ma_*` Bearer API key. Get one via the CLI:

```bash
npm install -g agent-media-cli
agent-media login
```

This stores the key at `~/.agent-media/credentials.json`. The bundled MCP server reads it via the `AGENT_MEDIA_API_KEY` environment variable; the plugin's `.mcp.json` does `${AGENT_MEDIA_API_KEY}` interpolation.

## Without the CLI

You can paste the `ma_*` token directly:

```bash
export AGENT_MEDIA_API_KEY="ma_..."
```

## How the key is used

- MCP server forwards it as `Authorization: Bearer ma_...` to `api.agent-media.ai`.
- Server resolves it to a `user_id` and runs every primitive against that account.
- Credits debit from the same account.

## Rotation

`agent-media logout && agent-media login` rotates the key. The old key keeps working for ~30 days unless explicitly revoked.

---

## Supporting file: reference/pacing.md

# Script pacing — how word count picks the duration

`make_ugc` does **not** ask you for a duration. It counts the words in your `script` and picks the take length for you:

| Words in your script | Duration you get | Credits |
| -------------------- | ---------------- | ------- |
| 1 – 11               | 5s               | 140     |
| 12 – 22              | 10s              | 280     |
| 23 +                 | 15s              | 420     |

That mapping is the server's `fitDuration()` — the same function the quote and the run both use, so the number `/quote` returns is the number you are charged.

**The boundaries are what matter.** A 12-word script is a 10-second video, not a 5-second one — and costs 280 credits, not 140. For a 5s take, stay at **11 words or fewer**.

Roughly 2.5 words per second is the natural TikTok talking-head cadence: too few words leaves dead air the model fills with filler "um"s, too many and it races and the lip-sync breaks.

## Longer scripts

There is no rejection for a long script. 23–33 words becomes a single 15s take. Past 33 the script is split into several takes stitched together, each priced by the same table — 33 words is the most a 15s take can hold (`round(15 × 2.2)`), so a 34-word script is two takes, not one long one. Call `/quote` first if you want the cost before spending.

## Examples

- 5s clip: *"This app completely changed my morning routine — try it."* (9 words)
- 10s clip: *"I've used this for two weeks and it saves me thirty minutes every morning. My coffee is still hot."* (20 words)
- 15s clip: *"Okay so I've been using this for two weeks now and it genuinely saves me thirty minutes every single morning, no joke — my coffee is still hot by the time I'm finished."* (33 words)

---

## Supporting file: reference/realism-rubric.md

# Realism

Every image and video is automatically enhanced so people look real, not AI-perfect — natural skin and lighting, candid UGC framing, believable imperfection. This happens server-side on every render; there is nothing to configure and no way (or need) to hand-write it.

## Choosing the look

Use `realism_target` to pick the overall look:

- `natural` — soft, natural daylight look.
- `commercial` — clean, brand-ready look.
- `raw_iphone` — unpolished, shot-on-phone look.

Just describe the person and scene in plain language in `description`; the realism enhancement is applied for you.
