@clawhub-api00-ec0c9e082a
Render a video from a script using only the media assets the caller provides (no stock visuals). Use for branded content where every frame must be on-brand —...
---
name: revid-script-with-custom-media
description: Render a video from a script using only the media assets the caller provides (no stock visuals). Use for branded content where every frame must be on-brand — product clips, brand b-roll, hand-shot footage.
metadata: {"openclaw":{"requires":{"config":["REVID_API_KEY"]}}}
---
# Script + your own media → branded video
For when the user has the script *and* the visuals. Revid only handles voice +
captions + assembly. No stock content is mixed in.
## When to use this skill
- The user has both a script and a set of clips/images they want used.
- Brand fidelity matters more than visual variety.
- For mixed (your media + stock to fill gaps), drop `media.useOnlyProvided` and
use [`revid-script-to-video`](../revid-script-to-video/SKILL.md) with `media.provided`.
## Inputs
| Field | Required | Notes |
|---|---|---|
| `text` | yes | The script |
| `media.provided[]` | yes | At least 3 items recommended; URLs must be public |
| `aspectRatio` | no | Default `9:16` |
| `voiceId` | no | Default voice if omitted |
`MediaItem` shape:
```json
{ "url": "https://…", "type": "image" | "video" | "audio", "title": "optional", "noReencode": false }
```
Use `type: "image"` for stills (Revid will optionally pan/zoom them via
`media.turnImagesIntoVideos`). Use `type: "video"` for clips. Keep clip
duration roughly comparable to the script length.
## Step-by-step
1. Validate every `media.provided[].url` returns 200 and a video/image
content-type.
2. Confirm enough assets for the script: rough rule = 1 asset per 8–10 s of
script, minimum 3.
3. POST `/render` with `media.useOnlyProvided: true`.
4. Poll `/status`.
## API call template
```http
POST /api/public/v3/render
Host: www.revid.ai
Content-Type: application/json
key: $REVID_API_KEY
```
```json
{
"workflow": "script-to-video",
"source": {
"text": "{SCRIPT}"
},
"aspectRatio": "9:16",
"voice": { "enabled": true, "voiceId": "aria-en-us", "stability": 0.6, "speed": 1.0 },
"captions": { "enabled": true, "position": "middle", "autoCrop": true },
"music": { "enabled": true, "syncWith": "beats" },
"media": {
"type": "custom",
"useOnlyProvided": true,
"turnImagesIntoVideos": true,
"mergeVideos": false,
"animation": "soft",
"provided": [
{ "url": "https://cdn.example.com/clip-1.mp4", "type": "video" },
{ "url": "https://cdn.example.com/clip-2.mp4", "type": "video" },
{ "url": "https://cdn.example.com/hero.jpg", "type": "image" }
]
},
"options": {
"summarizationPreference": "no-summarization",
"useOnlyProvidedMedia": true,
"soundEffects": false,
"hasToGenerateCover": true
},
"render": { "resolution": "1080p", "frameRate": 30 }
}
```
Both `media.useOnlyProvided` and `options.useOnlyProvidedMedia` should be
`true` — they belong to slightly different paths in the legacy code and setting
both is the safest way to forbid stock fill.
## Examples
- [`examples/branded-script.json`](examples/branded-script.json)
- [`examples/run.sh`](examples/run.sh)
## Failure modes
| Symptom | Fix |
|---|---|
| Final video pads with stock anyway | One of the two flags wasn't honored. Make sure both `media.useOnlyProvided: true` AND `options.useOnlyProvidedMedia: true` are set. |
| Video too short / dead air | Not enough assets. Add more `provided` items or set `mergeVideos: true` to loop the existing clips. |
| Wrong asset on a particular line | Pre-order assets in the array roughly in narrative order — Revid uses array order as a hint. |
| Asset URL 403 / 404 | Make sure assets are public (signed URLs work as long as they don't expire mid-render). |
## See also
- [`revid-script-to-video`](../revid-script-to-video/SKILL.md) for stock visuals.
- [`revid-shopify-product-promo`](../revid-shopify-product-promo/SKILL.md) for
the product-page variant.
FILE:examples/branded-script.json
{
"workflow": "script-to-video",
"source": {
"text": "Meet AeroPods Pro. Adaptive noise cancellation that learns your environment. 38 hours of total battery. IPX5 sweat-proofing. And spatial audio that puts you inside the music. $179. Free shipping today only."
},
"aspectRatio": "9:16",
"voice": { "enabled": true, "voiceId": "aria-en-us", "stability": 0.6, "speed": 1.0 },
"captions": { "enabled": true, "position": "middle", "autoCrop": true },
"music": { "enabled": true, "syncWith": "beats" },
"media": {
"type": "custom",
"useOnlyProvided": true,
"turnImagesIntoVideos": true,
"mergeVideos": false,
"animation": "soft",
"provided": [
{ "url": "https://cdn.example.com/aeropods/case-open.mp4", "type": "video" },
{ "url": "https://cdn.example.com/aeropods/runner.mp4", "type": "video" },
{ "url": "https://cdn.example.com/aeropods/hero-front.jpg", "type": "image" },
{ "url": "https://cdn.example.com/aeropods/charging.mp4", "type": "video" }
]
},
"options": {
"summarizationPreference": "no-summarization",
"useOnlyProvidedMedia": true,
"soundEffects": false,
"hasToGenerateCover": true
},
"render": { "resolution": "1080p", "frameRate": 30 }
}
FILE:examples/run.sh
#!/usr/bin/env bash
# Branded script + your own assets -> video.
# Usage: REVID_API_KEY=… ./run.sh
set -euo pipefail
: "?set REVID_API_KEY"
HERE="$(cd "$(dirname "$0")" && pwd)"
PAYLOAD=$(cat "$HERE/branded-script.json")
PID=$(curl -fsS https://www.revid.ai/api/public/v3/render \
-H "Content-Type: application/json" -H "key: $REVID_API_KEY" \
-d "$PAYLOAD" | jq -r .pid)
echo "pid=$PID"
while :; do
R=$(curl -fsSL "https://www.revid.ai/api/public/v3/status?pid=$PID" -H "key: $REVID_API_KEY")
S=$(echo "$R" | jq -r .status); echo " status=$S progress=$(echo "$R" | jq -r .progress)"
[ "$S" = "ready" ] && { echo "$R" | jq .; break; }
[ "$S" = "failed" ] && { echo "FAILED: $R"; exit 1; }
sleep 5
done
Generate a daily news short on a topic Revid researches itself. Use for a recurring "news of the day in <niche>" channel — the user only supplies the topic;...
---
name: revid-news-to-daily-short
description: Generate a daily news short on a topic Revid researches itself. Use for a recurring "news of the day in <niche>" channel — the user only supplies the topic; Revid fetches fresh news, writes the script, and produces the video.
metadata: {"openclaw":{"requires":{"config":["REVID_API_KEY"]}}}
---
# Topic / niche → daily news short
Recurring use case: feed a topic ("AI tools this week", "F1 race results",
"crypto headlines") and let Revid fetch live news, summarize it, and produce a
short. This is the right skill for *automated daily channels*.
## When to use this skill
- The user wants a **recurring daily short** on a topic — they don't have a
specific URL.
- They are happy with whatever Revid surfaces from the news for that topic.
- For a known article URL use [`revid-article-to-short`](../revid-article-to-short/SKILL.md).
- For a custom angle / angle the news doesn't cover use
[`revid-prompt-to-video`](../revid-prompt-to-video/SKILL.md).
## Inputs
| Field | Required | Notes |
|---|---|---|
| `prompt` | yes | The topic / niche |
| `aspectRatio` | no | Default `9:16` |
| `targetDuration` | no | Default 45 (s) |
| Cron / scheduling | external | This skill renders one video; loop externally for daily delivery. |
## Step-by-step
1. Build the payload (note: `options.fetchNews: true` is the magic switch).
2. POST `/render`.
3. Poll `/status`.
4. For a daily channel, schedule this in cron / GitHub Actions / Vercel Cron
and post the resulting `videoUrl` to the target social account. Use
`POST /api/public/v3/publish-now` if your Revid account has the relevant
socials connected.
## API call template
```http
POST /api/public/v3/render
Host: www.revid.ai
Content-Type: application/json
key: $REVID_API_KEY
```
```json
{
"workflow": "article-to-video",
"source": {
"prompt": "{TOPIC_OR_NICHE}"
},
"aspectRatio": "9:16",
"voice": { "enabled": true, "stability": 0.6, "speed": 1.0, "language": "en-US" },
"captions": { "enabled": true, "position": "middle", "autoCrop": true },
"music": { "enabled": true, "syncWith": "beats", "trackName": "news-upbeat" },
"media": {
"type": "stock-video",
"density": "medium",
"animation": "soft",
"quality": "pro",
"videoModel": "pro",
"imageModel": "good"
},
"options": {
"fetchNews": true,
"targetDuration": 45,
"summarizationPreference": "summarize",
"soundEffects": true,
"hasToGenerateCover": true,
"coverTextType": "headline"
},
"render": { "resolution": "1080p", "frameRate": 30 }
}
```
`options.fetchNews: true` tells Revid to crawl fresh news for the prompt
instead of using the prompt as the script directly.
## Daily automation example
```bash
# crontab — every day at 06:00
0 6 * * * /opt/revid/daily-news.sh "AI tools this week"
```
```bash
# daily-news.sh
TOPIC="?topic required"
PID=$(curl -fsS https://www.revid.ai/api/public/v3/render \
-H "Content-Type: application/json" -H "key: $REVID_API_KEY" \
-d "$(jq -n --arg p "$TOPIC" '{
workflow:"article-to-video",
source:{prompt:$p},
aspectRatio:"9:16",
voice:{enabled:true,stability:0.6},
captions:{enabled:true},
music:{enabled:true,syncWith:"beats"},
media:{type:"stock-video",density:"medium",quality:"pro",videoModel:"pro"},
options:{fetchNews:true,targetDuration:45,summarizationPreference:"summarize",hasToGenerateCover:true},
render:{resolution:"1080p"}
}')" | jq -r .pid)
# poll → publish via /publish-now or download the videoUrl …
```
## Examples
- [`examples/ai-tools-news.json`](examples/ai-tools-news.json)
- [`examples/run.sh`](examples/run.sh)
## Failure modes
| Symptom | Fix |
|---|---|
| News for niche topic is sparse / off-topic | Make the prompt more specific (e.g. `"AI coding tools released this week"`) and consider switching to [`revid-article-to-short`](../revid-article-to-short/SKILL.md) with a hand-picked URL. |
| Same news repeats day-over-day | Track `pid` history client-side and add a date phrase to the prompt: `"AI tools — week of 2026-04-26"`. |
| Tone too neutral / dry for the niche | Add `voice.voiceId` matching a known persona, and pass `source.stylePrompt: "Punchy, opinionated, end with a take."` |
## See also
- [`revid-article-to-short`](../revid-article-to-short/SKILL.md) for known URLs.
- [`revid-prompt-to-video`](../revid-prompt-to-video/SKILL.md) for ideas Revid
shouldn't research live.
FILE:examples/ai-tools-news.json
{
"workflow": "article-to-video",
"source": {
"prompt": "AI coding tools released this week"
},
"aspectRatio": "9:16",
"voice": { "enabled": true, "stability": 0.6, "speed": 1.0, "language": "en-US" },
"captions": { "enabled": true, "position": "middle", "autoCrop": true },
"music": { "enabled": true, "syncWith": "beats", "trackName": "news-upbeat" },
"media": {
"type": "stock-video",
"density": "medium",
"animation": "soft",
"quality": "pro",
"videoModel": "pro",
"imageModel": "good"
},
"options": {
"fetchNews": true,
"targetDuration": 45,
"summarizationPreference": "summarize",
"soundEffects": true,
"hasToGenerateCover": true,
"coverTextType": "headline"
},
"render": { "resolution": "1080p", "frameRate": 30 }
}
FILE:examples/run.sh
#!/usr/bin/env bash
# Topic -> daily news short. Pulls live news for the topic.
# Usage: REVID_API_KEY=… ./run.sh "AI coding tools released this week"
set -euo pipefail
: "?set REVID_API_KEY"
TOPIC="-AI coding tools released this week"
HERE="$(cd "$(dirname "$0")" && pwd)"
PAYLOAD=$(jq --arg p "$TOPIC" '.source.prompt=$p' "$HERE/ai-tools-news.json")
PID=$(curl -fsS https://www.revid.ai/api/public/v3/render \
-H "Content-Type: application/json" -H "key: $REVID_API_KEY" \
-d "$PAYLOAD" | jq -r .pid)
echo "pid=$PID"
while :; do
R=$(curl -fsSL "https://www.revid.ai/api/public/v3/status?pid=$PID" -H "key: $REVID_API_KEY")
S=$(echo "$R" | jq -r .status); echo " status=$S progress=$(echo "$R" | jq -r .progress)"
[ "$S" = "ready" ] && { echo "$R" | jq .; break; }
[ "$S" = "failed" ] && { echo "FAILED: $R"; exit 1; }
sleep 5
done
Turn a PDF (whitepaper, ebook chapter, slide deck export, research paper) into a short summary video. Use when the source is a PDF URL or a PDF the agent can...
---
name: revid-pdf-to-video
description: Turn a PDF (whitepaper, ebook chapter, slide deck export, research paper) into a short summary video. Use when the source is a PDF URL or a PDF the agent can upload to public storage first.
metadata: {"openclaw":{"requires":{"config":["REVID_API_KEY"]}}}
---
# PDF → summary video
Take a PDF URL and produce a short summary video. Internally this routes
through `article-to-video` once the PDF text has been extracted by Revid's
scraper.
## When to use this skill
- Source is a public PDF URL (whitepaper, paper, ebook, slide export).
- Goal is a 30–90 s summary, not a full reading.
- For HTML articles use [`revid-article-to-short`](../revid-article-to-short/SKILL.md).
- For local PDFs the agent must first upload to public storage (S3, Supabase
Storage, etc.) so Revid can fetch it.
## Inputs
| Field | Required | Notes |
|---|---|---|
| `url` | yes | Public PDF URL (must be reachable, no auth) |
| `aspectRatio` | no | Default `9:16` |
| `targetDuration` | no | Default 60 (s); raise to 90 s for dense papers |
## Step-by-step
1. Confirm the URL ends in `.pdf` or returns `Content-Type: application/pdf`.
2. Set `source.scrapingPrompt` to bias the summary toward what the user cares
about ("Focus on the methodology section", "Focus on the executive summary",
"Pull the 3 biggest takeaways").
3. POST `/render` with the payload below.
4. Poll `/status`.
## API call template
```http
POST /api/public/v3/render
Host: www.revid.ai
Content-Type: application/json
key: $REVID_API_KEY
```
```json
{
"workflow": "article-to-video",
"source": {
"url": "{PDF_URL}",
"scrapingPrompt": "Extract the executive summary and the 3 biggest takeaways. Skip references and appendices."
},
"aspectRatio": "9:16",
"voice": { "enabled": true, "stability": 0.65, "speed": 0.95, "language": "en-US" },
"captions": { "enabled": true, "position": "middle", "autoCrop": true },
"music": { "enabled": true, "syncWith": "beats" },
"media": {
"type": "stock-video",
"density": "medium",
"animation": "soft",
"quality": "pro",
"imageModel": "good",
"videoModel": "pro"
},
"options": {
"targetDuration": 60,
"summarizationPreference": "summarize",
"soundEffects": true,
"hasToGenerateCover": true,
"coverTextType": "title"
},
"render": { "resolution": "1080p", "frameRate": 30 }
}
```
## Examples
- [`examples/whitepaper.json`](examples/whitepaper.json)
- [`examples/run.sh`](examples/run.sh)
## Failure modes
| Symptom | Fix |
|---|---|
| `scrape failed` | PDF behind auth or login wall. Re-host the PDF on public storage. |
| Summary skips the section the user cares about | Tighten `scrapingPrompt` (be specific: section names, page ranges). |
| Visuals are abstract / off-topic | PDFs rarely have crawlable hero images. Pre-render a few key figures as images and pass them in `media.provided`. |
| Voice rushes through technical terms | Lower `voice.speed` to `0.9`. |
## See also
- [`revid-article-to-short`](../revid-article-to-short/SKILL.md) for HTML.
- [`revid-script-with-custom-media`](../revid-script-with-custom-media/SKILL.md)
for full visual control.
FILE:examples/run.sh
#!/usr/bin/env bash
# PDF URL -> summary video.
# Usage: REVID_API_KEY=… ./run.sh <pdf-url>
set -euo pipefail
: "?set REVID_API_KEY"
URL="?pdf url required"
HERE="$(cd "$(dirname "$0")" && pwd)"
PAYLOAD=$(jq --arg u "$URL" '.source.url=$u' "$HERE/whitepaper.json")
PID=$(curl -fsS https://www.revid.ai/api/public/v3/render \
-H "Content-Type: application/json" -H "key: $REVID_API_KEY" \
-d "$PAYLOAD" | jq -r .pid)
echo "pid=$PID"
while :; do
R=$(curl -fsSL "https://www.revid.ai/api/public/v3/status?pid=$PID" -H "key: $REVID_API_KEY")
S=$(echo "$R" | jq -r .status); echo " status=$S progress=$(echo "$R" | jq -r .progress)"
[ "$S" = "ready" ] && { echo "$R" | jq .; break; }
[ "$S" = "failed" ] && { echo "FAILED: $R"; exit 1; }
sleep 5
done
FILE:examples/whitepaper.json
{
"workflow": "article-to-video",
"source": {
"url": "https://arxiv.org/pdf/2401.00000.pdf",
"scrapingPrompt": "Extract the abstract and the 3 most surprising findings. Skip references and appendices."
},
"aspectRatio": "9:16",
"voice": { "enabled": true, "stability": 0.65, "speed": 0.95, "language": "en-US" },
"captions": { "enabled": true, "position": "middle", "autoCrop": true },
"music": { "enabled": true, "syncWith": "beats" },
"media": {
"type": "stock-video",
"density": "medium",
"animation": "soft",
"quality": "pro",
"imageModel": "good",
"videoModel": "pro"
},
"options": {
"targetDuration": 60,
"summarizationPreference": "summarize",
"soundEffects": true,
"hasToGenerateCover": true,
"coverTextType": "title"
},
"render": { "resolution": "1080p", "frameRate": 30 }
}
Turn a one-line idea into a full short video — Revid writes the script, picks visuals, and assembles the cut. Use when the user has a topic but no script.
---
name: revid-prompt-to-video
description: Turn a one-line idea into a full short video — Revid writes the script, picks visuals, and assembles the cut. Use when the user has a topic but no script.
metadata: {"openclaw":{"requires":{"config":["REVID_API_KEY"]}}}
---
# Idea prompt → video
The lowest-input skill. The user types one line ("Why honey never spoils") and
Revid handles everything: script, visuals, voice, music, cuts.
## When to use this skill
- Source is a topic, question, or one-line concept — *no script*.
- The user is OK letting the AI choose the angle and structure.
- For a known script, use [`revid-script-to-video`](../revid-script-to-video/SKILL.md).
- For an idea + brand voice, use
[`revid-product-description-to-ad`](../revid-product-description-to-ad/SKILL.md).
## Inputs
| Field | Required | Notes |
|---|---|---|
| `prompt` | yes | The idea (one or two sentences) |
| `stylePrompt` | no | Optional tone notes |
| `durationSeconds` | no | Default 35 (s) |
| `aspectRatio` | no | Default `9:16` |
## Step-by-step
1. Validate `prompt` is non-empty.
2. POST `/render`.
3. Poll `/status`.
## API call template
```http
POST /api/public/v3/render
Host: www.revid.ai
Content-Type: application/json
key: $REVID_API_KEY
```
```json
{
"workflow": "prompt-to-video",
"source": {
"prompt": "{ONE_LINE_IDEA}",
"stylePrompt": "{OPTIONAL_TONE_NOTES}",
"durationSeconds": 35
},
"aspectRatio": "9:16",
"voice": { "enabled": true, "stability": 0.6, "speed": 1.0, "language": "en-US" },
"captions": { "enabled": true, "position": "middle", "autoCrop": true },
"music": { "enabled": true, "syncWith": "beats" },
"media": {
"type": "stock-video",
"density": "medium",
"animation": "soft",
"quality": "pro",
"videoModel": "pro",
"imageModel": "good"
},
"options": {
"promptTargetDuration": 35,
"summarizationPreference": "summarizeIfLong",
"soundEffects": true,
"hasToGenerateCover": true
},
"render": { "resolution": "1080p", "frameRate": 30 }
}
```
## Examples
- [`examples/honey-prompt.json`](examples/honey-prompt.json)
- [`examples/run.sh`](examples/run.sh)
## Failure modes
| Symptom | Fix |
|---|---|
| Script angle is generic ("Did you know…") | Add `stylePrompt` with a specific angle: `"Open with a contrarian claim. End with a question that invites comments."` |
| Off-niche visuals | Mention concrete subjects in the prompt: `"Why honey never spoils — show beehives, ancient Egyptian jars, microscope close-ups of crystallized honey."` |
| Too long / too short | Use `durationSeconds` AND `options.promptTargetDuration` together (some legacy paths only read one). |
## See also
- [`revid-script-to-video`](../revid-script-to-video/SKILL.md) when you have the words.
- [`revid-product-description-to-ad`](../revid-product-description-to-ad/SKILL.md)
for ads.
FILE:examples/honey-prompt.json
{
"workflow": "prompt-to-video",
"source": {
"prompt": "Why honey never spoils.",
"stylePrompt": "Open with a punchy archaeological hook. Walk through the chemistry in plain language. End with a takeaway.",
"durationSeconds": 35
},
"aspectRatio": "9:16",
"voice": { "enabled": true, "stability": 0.6, "speed": 1.0, "language": "en-US" },
"captions": { "enabled": true, "position": "middle", "autoCrop": true },
"music": { "enabled": true, "syncWith": "beats" },
"media": {
"type": "stock-video",
"density": "medium",
"animation": "soft",
"quality": "pro",
"videoModel": "pro",
"imageModel": "good"
},
"options": {
"promptTargetDuration": 35,
"summarizationPreference": "summarizeIfLong",
"soundEffects": true,
"hasToGenerateCover": true
},
"render": { "resolution": "1080p", "frameRate": 30 }
}
FILE:examples/run.sh
#!/usr/bin/env bash
# Idea prompt -> video.
# Usage: REVID_API_KEY=… ./run.sh "Your one-line idea"
set -euo pipefail
: "?set REVID_API_KEY"
PROMPT="-Why honey never spoils."
HERE="$(cd "$(dirname "$0")" && pwd)"
PAYLOAD=$(jq --arg p "$PROMPT" '.source.prompt=$p' "$HERE/honey-prompt.json")
PID=$(curl -fsS https://www.revid.ai/api/public/v3/render \
-H "Content-Type: application/json" -H "key: $REVID_API_KEY" \
-d "$PAYLOAD" | jq -r .pid)
echo "pid=$PID"
while :; do
R=$(curl -fsSL "https://www.revid.ai/api/public/v3/status?pid=$PID" -H "key: $REVID_API_KEY")
S=$(echo "$R" | jq -r .status); echo " status=$S progress=$(echo "$R" | jq -r .progress)"
[ "$S" = "ready" ] && { echo "$R" | jq .; break; }
[ "$S" = "failed" ] && { echo "FAILED: $R"; exit 1; }
sleep 5
done
Turn an already-written script into a video with voiceover, auto-cut stock visuals, and captions. Use when the user has the words and wants Revid to handle p...
---
name: revid-script-to-video
description: Turn an already-written script into a video with voiceover, auto-cut stock visuals, and captions. Use when the user has the words and wants Revid to handle production.
metadata: {"openclaw":{"requires":{"config":["REVID_API_KEY"]}}}
---
# Script → video
The script-to-video workflow is the lowest-friction path: you bring the words,
Revid brings the visuals + voice + captions + edit.
## When to use this skill
- The user pastes a finished script (or generates one in-conversation).
- They want full creative control over the *words*.
- They are happy with stock visuals (otherwise see
[`revid-script-with-custom-media`](../revid-script-with-custom-media/SKILL.md)).
- For an idea-to-video flow, use
[`revid-prompt-to-video`](../revid-prompt-to-video/SKILL.md).
## Inputs
| Field | Required | Notes |
|---|---|---|
| `text` | yes | The script. Use line breaks for scene boundaries. |
| `aspectRatio` | no | Default `9:16` |
| `voiceId` | no | Pick to match the script tone |
| `targetDuration` | no | Auto-derived from script length if omitted |
## Step-by-step
1. Validate `text` is non-trivial (>30 words) and within practical limits
(~1500 words for a 5-min video).
2. POST `/render`.
3. Poll `/status`.
## API call template
```http
POST /api/public/v3/render
Host: www.revid.ai
Content-Type: application/json
key: $REVID_API_KEY
```
```json
{
"workflow": "script-to-video",
"source": { "text": "{SCRIPT}" },
"aspectRatio": "9:16",
"voice": { "enabled": true, "voiceId": "aria-en-us", "stability": 0.6, "speed": 1.0, "language": "en-US" },
"captions": { "enabled": true, "position": "middle", "autoCrop": true },
"music": { "enabled": true, "syncWith": "beats" },
"media": {
"type": "stock-video",
"density": "medium",
"animation": "soft",
"quality": "pro",
"imageModel": "good",
"videoModel": "pro"
},
"options": {
"summarizationPreference": "no-summarization",
"soundEffects": true,
"hasToGenerateCover": true
},
"render": { "resolution": "1080p", "frameRate": 30 }
}
```
`summarizationPreference: "no-summarization"` — the user wrote the script for a
reason. Don't paraphrase it.
## Examples
- [`examples/honey-script.json`](examples/honey-script.json)
- [`examples/run.sh`](examples/run.sh)
## Failure modes
| Symptom | Fix |
|---|---|
| Script too long → exceeds context | Either split into multiple `/render` calls (one per chapter) or set `summarizationPreference: "summarize"`. |
| Voice mispronounces brand names | Inline phonetic spelling in the script ("Revid (rev-id)"). |
| Visuals don't match niche topic | Pre-author a few key shots and switch to [`revid-script-with-custom-media`](../revid-script-with-custom-media/SKILL.md). |
| Music drowns the voice | Lower music duck — currently no direct knob; switch `music.enabled: false` and add ambient sound effects via `options.soundEffects: true`. |
## See also
- [`revid-script-with-custom-media`](../revid-script-with-custom-media/SKILL.md)
for full visual control.
- [`revid-prompt-to-video`](../revid-prompt-to-video/SKILL.md) when you want
Revid to write the script too.
- [`revid-tweet-to-talking-head`](../revid-tweet-to-talking-head/SKILL.md) for
short scripts with an avatar.
FILE:examples/honey-script.json
{
"workflow": "script-to-video",
"source": {
"text": "Honey never spoils. Archaeologists have opened 3,000-year-old jars from Egyptian tombs and found honey still safe to eat. The reason is chemistry: honey is roughly 80% sugar and only 18% water — too dry for bacteria to grow. Bees also add an enzyme that produces hydrogen peroxide, a natural preservative. As long as it's sealed, your honey could outlive you. So next time you find a forgotten jar in the back of the cupboard — you don't have to throw it out."
},
"aspectRatio": "9:16",
"voice": { "enabled": true, "voiceId": "aria-en-us", "stability": 0.6, "speed": 1.0, "language": "en-US" },
"captions": { "enabled": true, "position": "middle", "autoCrop": true },
"music": { "enabled": true, "syncWith": "beats" },
"media": {
"type": "stock-video",
"density": "medium",
"animation": "soft",
"quality": "pro",
"imageModel": "good",
"videoModel": "pro"
},
"options": {
"summarizationPreference": "no-summarization",
"soundEffects": true,
"hasToGenerateCover": true
},
"render": { "resolution": "1080p", "frameRate": 30 }
}
FILE:examples/run.sh
#!/usr/bin/env bash
# Script text -> video.
# Usage: REVID_API_KEY=… ./run.sh # uses bundled example
# REVID_API_KEY=… ./run.sh ./script.txt
set -euo pipefail
: "?set REVID_API_KEY"
HERE="$(cd "$(dirname "$0")" && pwd)"
PAYLOAD_FILE="$HERE/honey-script.json"
if [ "-" ] && [ -f "$1" ]; then
TXT=$(cat "$1")
PAYLOAD=$(jq --arg t "$TXT" '.source.text=$t' "$PAYLOAD_FILE")
else
PAYLOAD=$(cat "$PAYLOAD_FILE")
fi
PID=$(curl -fsS https://www.revid.ai/api/public/v3/render \
-H "Content-Type: application/json" -H "key: $REVID_API_KEY" \
-d "$PAYLOAD" | jq -r .pid)
echo "pid=$PID"
while :; do
R=$(curl -fsSL "https://www.revid.ai/api/public/v3/status?pid=$PID" -H "key: $REVID_API_KEY")
S=$(echo "$R" | jq -r .status); echo " status=$S progress=$(echo "$R" | jq -r .progress)"
[ "$S" = "ready" ] && { echo "$R" | jq .; break; }
[ "$S" = "failed" ] && { echo "FAILED: $R"; exit 1; }
sleep 5
done
Turn an X/Twitter/LinkedIn post (URL or pasted thread text) into a talking-head video that delivers the take. Use when a creator wants to repurpose a viral p...
---
name: revid-tweet-to-talking-head
description: Turn an X/Twitter/LinkedIn post (URL or pasted thread text) into a talking-head video that delivers the take. Use when a creator wants to repurpose a viral post as a short-form video.
metadata: {"openclaw":{"requires":{"config":["REVID_API_KEY"]}}}
---
# Tweet / X / LinkedIn post → talking-head video
Take a single post or a thread and produce a 20–45 s talking-head video where
an avatar reads the take.
## When to use this skill
- Source is a tweet URL, X thread URL, LinkedIn post URL, or pasted thread text.
- Output is a **talking-head** delivering the post (avatar + voiceover +
captions + minimal background motion).
- For an *edited summary with stock visuals* of an article, use
[`revid-article-to-short`](../revid-article-to-short/SKILL.md) instead.
## Inputs
| Field | Required | Notes |
|---|---|---|
| `text` *or* `url` | yes | Either the pasted thread (preferred — no scraping) or the post URL. |
| `avatar.url` *or* `characterIds[]` | yes | The face. |
| `aspectRatio` | no | Default `9:16` |
| `targetDuration` | no | Default 30 (s) |
## Step-by-step
1. If you have the tweet/thread text, prefer `script-to-video` (no scraping
risk — many social platforms block bots).
2. If you only have a URL, use `article-to-video` with a tight `scrapingPrompt`.
3. Either way, attach the `avatar` block + a single `characterId`.
4. POST `/render`.
5. Poll `/status`.
## API call template — pasted thread (preferred)
```http
POST /api/public/v3/render
Host: www.revid.ai
Content-Type: application/json
key: $REVID_API_KEY
```
```json
{
"workflow": "script-to-video",
"source": {
"text": "{TWEET_OR_THREAD_TEXT}"
},
"aspectRatio": "9:16",
"avatar": {
"enabled": true,
"url": "{AVATAR_IMAGE_URL}",
"removeBackground": true
},
"voice": { "enabled": true, "voiceId": "aria-en-us", "stability": 0.65, "speed": 1.05 },
"captions": { "enabled": true, "position": "middle", "autoCrop": true },
"music": { "enabled": false },
"media": {
"type": "moving-image",
"density": "low",
"animation": "soft",
"placeAvatarInContext": true
},
"options": {
"targetDuration": 30,
"summarizationPreference": "no-summarization",
"hasToGenerateCover": true,
"coverTextType": "first-line"
},
"render": { "resolution": "1080p", "frameRate": 30 }
}
```
`summarizationPreference: "no-summarization"` — for tweets, the original
phrasing IS the value. Don't paraphrase.
## API call template — URL (fallback)
```json
{
"workflow": "article-to-video",
"source": {
"url": "{POST_URL}",
"scrapingPrompt": "Extract only the original post text and the thread author. Ignore replies, reposts, and side panels."
},
"aspectRatio": "9:16",
"avatar": { "enabled": true, "url": "{AVATAR_IMAGE_URL}", "removeBackground": true },
"voice": { "enabled": true, "voiceId": "aria-en-us", "stability": 0.65 },
"captions":{ "enabled": true, "position": "middle" },
"music": { "enabled": false },
"media": { "type": "moving-image", "density": "low", "animation": "soft" },
"options": { "targetDuration": 30, "summarizationPreference": "summarizeIfLong" },
"render": { "resolution": "1080p" }
}
```
## Examples
- [`examples/thread-text.json`](examples/thread-text.json)
- [`examples/run.sh`](examples/run.sh)
## Failure modes
| Symptom | Fix |
|---|---|
| `scrape failed` on URL form | Switch to the pasted-thread template — copy the post body into `source.text`. |
| Lip-sync drifts on multi-tweet thread | Lower `targetDuration` to 25 s, or set `summarizationPreference: "summarize"`. |
| Avatar reads in the wrong tone | Set `voice.voiceId` explicitly to a voice that matches the persona; default voice is rarely right for personality-driven content. |
| Tweet contains URLs / @mentions / hashtags | The voice will read them aloud awkwardly. Pre-clean the text: strip raw URLs, replace `@handle` with the person's name, and remove standalone hashtags (or keep one as a sign-off). |
## See also
- [`revid-blog-to-avatar-video`](../revid-blog-to-avatar-video/SKILL.md) — same
pattern, longer source.
- [`revid-script-to-video`](../revid-script-to-video/SKILL.md).
- [`revid-api-foundations`](../revid-api-foundations/SKILL.md).
FILE:examples/run.sh
#!/usr/bin/env bash
# Tweet text + avatar -> talking-head video.
# Usage: REVID_API_KEY=… ./run.sh ./tweet.txt <avatar-image-url>
set -euo pipefail
: "?set REVID_API_KEY"
TXT_FILE="?path to tweet text required"
AVATAR="?avatar image url required"
HERE="$(cd "$(dirname "$0")" && pwd)"
TXT=$(cat "$TXT_FILE")
PAYLOAD=$(jq --arg t "$TXT" --arg a "$AVATAR" \
'.source.text=$t | .avatar.url=$a' \
"$HERE/thread-text.json")
PID=$(curl -fsS https://www.revid.ai/api/public/v3/render \
-H "Content-Type: application/json" -H "key: $REVID_API_KEY" \
-d "$PAYLOAD" | jq -r .pid)
echo "pid=$PID"
while :; do
R=$(curl -fsSL "https://www.revid.ai/api/public/v3/status?pid=$PID" -H "key: $REVID_API_KEY")
S=$(echo "$R" | jq -r .status); echo " status=$S progress=$(echo "$R" | jq -r .progress)"
[ "$S" = "ready" ] && { echo "$R" | jq .; break; }
[ "$S" = "failed" ] && { echo "FAILED: $R"; exit 1; }
sleep 5
done
FILE:examples/thread-text.json
{
"workflow": "script-to-video",
"source": {
"text": "Most creators are doing shorts wrong. They write a script, film it, then add captions. The pros do the opposite: they write the captions first, then build the script around them. Here's why that works — captions are what the algorithm reads. If your hook isn't on screen in the first second, the algorithm doesn't even know it exists. Try it on your next video. Caption-first scripts get 2-3x the watch time."
},
"aspectRatio": "9:16",
"avatar": {
"enabled": true,
"url": "https://cdn.example.com/avatars/maya.jpg",
"removeBackground": true
},
"voice": { "enabled": true, "voiceId": "aria-en-us", "stability": 0.65, "speed": 1.05 },
"captions": { "enabled": true, "position": "middle", "autoCrop": true },
"music": { "enabled": false },
"media": {
"type": "moving-image",
"density": "low",
"animation": "soft",
"placeAvatarInContext": true
},
"options": {
"targetDuration": 30,
"summarizationPreference": "no-summarization",
"hasToGenerateCover": true,
"coverTextType": "first-line"
},
"render": { "resolution": "1080p", "frameRate": 30 }
}
Turn a product description (free-form text — no URL needed) into a punchy 15–30 second AI-generated ad with hooks, CTA, and visuals. Use when the user pastes...
---
name: revid-product-description-to-ad
description: Turn a product description (free-form text — no URL needed) into a punchy 15–30 second AI-generated ad with hooks, CTA, and visuals. Use when the user pastes copy or specs but doesn't have a live page to scrape.
metadata: {"openclaw":{"requires":{"config":["REVID_API_KEY"]}}}
---
# Product description → AI ad
Take a paragraph (or bullet list) describing a product and produce a polished
short-form ad. The `ad-generator` workflow writes the hook + body + CTA itself
based on the description.
## When to use this skill
- The user pastes a product description, spec sheet, or feature list — *not* a
URL.
- They want hook → benefit → CTA structure (a *commercial*).
- They have no avatar / talking-head requirement (use
[`revid-blog-to-avatar-video`](../revid-blog-to-avatar-video/SKILL.md)
for that).
- For a live URL, use
[`revid-shopify-product-promo`](../revid-shopify-product-promo/SKILL.md).
## Inputs
| Field | Required | Notes |
|---|---|---|
| `prompt` | yes | The product description (the AI uses it as the brief) |
| `stylePrompt` | no | Optional brand voice notes (e.g. *"Apple-like, calm, premium"*) |
| `aspectRatio` | no | Default `9:16` |
| `targetDuration` | no | Default 22 (s) |
| `mediaItems` | no | If you have product images, pass them in `media.provided` |
## Step-by-step
1. Validate `prompt` has at least ~30 words (otherwise the ad is too thin).
2. Build the payload below; if product images were provided, slot them into
`media.provided` and set `media.useOnlyProvided: false` (mix with stock).
3. POST `/render`.
4. Poll `/status`.
5. Return `videoUrl`.
## API call template
```http
POST /api/public/v3/render
Host: www.revid.ai
Content-Type: application/json
key: $REVID_API_KEY
```
```json
{
"workflow": "ad-generator",
"source": {
"prompt": "{PRODUCT_DESCRIPTION}",
"stylePrompt": "{OPTIONAL_BRAND_VOICE_NOTES}",
"durationSeconds": 22
},
"aspectRatio": "9:16",
"voice": { "enabled": true, "stability": 0.55, "speed": 1.05, "language": "en-US" },
"captions": { "enabled": true, "position": "middle", "autoCrop": true },
"music": { "enabled": true, "syncWith": "beats", "trackName": "ad-energetic" },
"media": {
"type": "stock-video",
"density": "high",
"animation": "dynamic",
"quality": "ultra",
"imageModel": "ultra",
"videoModel": "ultra",
"turnImagesIntoVideos": true,
"applyStyleTransfer": false,
"provided": []
},
"options": {
"targetDuration": 22,
"promptTargetDuration": 22,
"summarizationPreference": "summarizeIfLong",
"soundEffects": true,
"addStickers": true,
"hasToGenerateCover": true,
"coverTextType": "hook"
},
"render": { "resolution": "1080p", "frameRate": 30 }
}
```
`ad-generator` defaults to higher visual quality than article-to-video because
ads compete on the first second. If credits are tight drop `quality` to `pro`.
## Examples
- [`examples/aeropods-ad.json`](examples/aeropods-ad.json) — payload with brand
notes.
- [`examples/run.sh`](examples/run.sh) — accepts description as a file or
positional arg.
## Failure modes
| Symptom | Fix |
|---|---|
| Hook is generic | Make `prompt` specific. `"Wireless earbuds"` → meh. `"Wireless earbuds with adaptive ANC and 38h battery for $179"` → strong. |
| Ad reads like a feature list, not a hook | Add `stylePrompt: "Lead with a question or emotional hook. Save specs for the middle. End with the price + a single CTA."` |
| Visuals don't match the product | Pass real product images via `media.provided: [{ url, type: "image" }]`. The AI will weave them in. |
| Voice rushes | Lower `voice.speed` to `0.95`. |
| Too many on-screen stickers | `options.addStickers: false`. |
## See also
- [`revid-shopify-product-promo`](../revid-shopify-product-promo/SKILL.md) — same
goal but starts from a URL.
- [`revid-script-with-custom-media`](../revid-script-with-custom-media/SKILL.md)
— full creative control.
FILE:examples/aeropods-ad.json
{
"workflow": "ad-generator",
"source": {
"prompt": "AeroPods Pro — wireless earbuds with adaptive active noise cancellation, 38 hours of total battery life with the case, IPX5 sweat resistance, USB-C wireless charging, and spatial audio. $179. Designed for commuters and athletes who want studio-grade sound without the cable mess.",
"stylePrompt": "Lead with a sharp question hook. Then 2 short benefit beats. Close with the price and a single CTA. Voice tone: confident, calm, premium — Apple-like.",
"durationSeconds": 22
},
"aspectRatio": "9:16",
"voice": { "enabled": true, "stability": 0.55, "speed": 1.05, "language": "en-US" },
"captions": { "enabled": true, "position": "middle", "autoCrop": true },
"music": { "enabled": true, "syncWith": "beats", "trackName": "ad-energetic" },
"media": {
"type": "stock-video",
"density": "high",
"animation": "dynamic",
"quality": "ultra",
"imageModel": "ultra",
"videoModel": "ultra",
"turnImagesIntoVideos": true,
"applyStyleTransfer": false,
"provided": []
},
"options": {
"targetDuration": 22,
"promptTargetDuration": 22,
"summarizationPreference": "summarizeIfLong",
"soundEffects": true,
"addStickers": true,
"hasToGenerateCover": true,
"coverTextType": "hook"
},
"render": { "resolution": "1080p", "frameRate": 30 }
}
FILE:examples/run.sh
#!/usr/bin/env bash
# Product description -> AI ad video.
# Usage: REVID_API_KEY=… ./run.sh # uses bundled example
# REVID_API_KEY=… ./run.sh ./desc.txt
set -euo pipefail
: "?set REVID_API_KEY"
HERE="$(cd "$(dirname "$0")" && pwd)"
PAYLOAD_FILE="$HERE/aeropods-ad.json"
if [ "-" ] && [ -f "$1" ]; then
DESC=$(cat "$1")
PAYLOAD=$(jq --arg p "$DESC" '.source.prompt=$p' "$PAYLOAD_FILE")
else
PAYLOAD=$(cat "$PAYLOAD_FILE")
fi
PID=$(curl -fsS https://www.revid.ai/api/public/v3/render \
-H "Content-Type: application/json" -H "key: $REVID_API_KEY" \
-d "$PAYLOAD" | jq -r .pid)
echo "pid=$PID"
while :; do
R=$(curl -fsSL "https://www.revid.ai/api/public/v3/status?pid=$PID" -H "key: $REVID_API_KEY")
S=$(echo "$R" | jq -r .status); echo " status=$S progress=$(echo "$R" | jq -r .progress)"
[ "$S" = "ready" ] && { echo "$R" | jq .; break; }
[ "$S" = "failed" ] && { echo "FAILED: $R"; exit 1; }
sleep 5
done
Turn any news article or long-form post URL into a 30–60 second 9:16 short with stock visuals, narration, and captions. Use when the user shares a link and w...
---
name: revid-article-to-short
description: Turn any news article or long-form post URL into a 30–60 second 9:16 short with stock visuals, narration, and captions. Use when the user shares a link and wants an edited summary, not a talking-head.
metadata: {"openclaw":{"requires":{"config":["REVID_API_KEY"]}}}
---
# Article / news → short
Take any URL with a substantial article body and produce a vertical short with
voiceover + auto-cut stock b-roll + captions.
## When to use this skill
- Source is a news article, long-form blog, press release, or essay.
- Output goal: an **edited summary**, voiceover + visuals, 30–60 s.
- The user does NOT want a talking-head (use
[`revid-blog-to-avatar-video`](../revid-blog-to-avatar-video/SKILL.md) for that).
- For e-commerce product pages prefer
[`revid-shopify-product-promo`](../revid-shopify-product-promo/SKILL.md) — same
workflow but tuned defaults.
## Inputs
| Field | Required | Notes |
|---|---|---|
| `url` | yes | Article URL |
| `aspectRatio` | no | Default `9:16` |
| `targetDuration` | no | Default 45 s |
| `language` | no | Auto-detected; override for non-English |
## Step-by-step
1. Validate the URL.
2. POST the payload below.
3. Poll `/status` (canonical loop in the Polling section below).
4. Return `videoUrl`.
## API call template
```http
POST /api/public/v3/render
Host: www.revid.ai
Content-Type: application/json
key: $REVID_API_KEY
```
```json
{
"workflow": "article-to-video",
"source": {
"url": "{ARTICLE_URL}",
"scrapingPrompt": "Summarize the article body. Skip ads, related links, navigation, and footer."
},
"aspectRatio": "9:16",
"voice": { "enabled": true, "stability": 0.6, "speed": 1.0, "language": "en-US" },
"captions": { "enabled": true, "position": "middle", "autoCrop": true },
"music": { "enabled": true, "syncWith": "beats" },
"media": {
"type": "stock-video",
"density": "medium",
"animation": "soft",
"quality": "pro",
"videoModel": "pro",
"imageModel": "good"
},
"options": {
"targetDuration": 45,
"summarizationPreference": "summarize",
"soundEffects": true,
"hasToGenerateCover": true,
"coverTextType": "headline"
},
"render": { "resolution": "1080p", "frameRate": 30 }
}
```
## Examples
- [`examples/article-techreview.json`](examples/article-techreview.json)
- [`examples/run.sh`](examples/run.sh)
## Polling
After `POST /render`, poll until `status === "ready"`:
```bash
PID="<pid-from-render>"
while :; do
R=$(curl -fsSL "https://www.revid.ai/api/public/v3/status?pid=$PID" \
-H "key: $REVID_API_KEY")
S=$(echo "$R" | jq -r .status)
case "$S" in
ready) echo "$R" | jq .; break ;;
failed) echo "FAILED: $R"; exit 1 ;;
*) sleep 5 ;;
esac
done
```
In production prefer setting `webhookUrl` in the request body and skip polling.
## Failure modes
| Symptom | Fix |
|---|---|
| `scrape failed` | Pre-fetch the article body server-side and switch to [`revid-script-to-video`](../revid-script-to-video/SKILL.md) with the body in `source.text`. |
| Off-topic stock visuals | Pass a tighter `scrapingPrompt` (e.g. *"Focus on the financial markets angle, not the company history"*) and lower `media.density: "low"`. |
| Wrong language detected | Set `voice.language` and `options.language` explicitly. |
| Captions clip subjects | `captions.position: "top"`. |
## See also
- [`revid-shopify-product-promo`](../revid-shopify-product-promo/SKILL.md)
- [`revid-news-to-daily-short`](../revid-news-to-daily-short/SKILL.md) for
*generating* news from a topic vs summarizing a known URL.
- [`revid-pdf-to-video`](../revid-pdf-to-video/SKILL.md) for PDFs instead of HTML.
FILE:examples/article-techreview.json
{
"workflow": "article-to-video",
"source": {
"url": "https://techreview.io/ai-tools-2026",
"scrapingPrompt": "Summarize the article body. Skip ads, related links, navigation, and footer."
},
"aspectRatio": "9:16",
"voice": { "enabled": true, "stability": 0.6, "speed": 1.0, "language": "en-US" },
"captions": { "enabled": true, "position": "middle", "autoCrop": true },
"music": { "enabled": true, "syncWith": "beats" },
"media": {
"type": "stock-video",
"density": "medium",
"animation": "soft",
"quality": "pro",
"videoModel": "pro",
"imageModel": "good"
},
"options": {
"targetDuration": 45,
"summarizationPreference": "summarize",
"soundEffects": true,
"hasToGenerateCover": true,
"coverTextType": "headline"
},
"render": { "resolution": "1080p", "frameRate": 30 }
}
FILE:examples/run.sh
#!/usr/bin/env bash
# Article URL -> 9:16 short.
# Usage: REVID_API_KEY=… ./run.sh <article-url>
set -euo pipefail
: "?set REVID_API_KEY"
URL="?article url required"
HERE="$(cd "$(dirname "$0")" && pwd)"
PAYLOAD="$(jq --arg u "$URL" '.source.url=$u' "$HERE/article-techreview.json")"
PID=$(curl -fsS https://www.revid.ai/api/public/v3/render \
-H "Content-Type: application/json" -H "key: $REVID_API_KEY" \
-d "$PAYLOAD" | jq -r .pid)
echo "pid=$PID"
while :; do
R=$(curl -fsSL "https://www.revid.ai/api/public/v3/status?pid=$PID" -H "key: $REVID_API_KEY")
S=$(echo "$R" | jq -r .status); echo " status=$S progress=$(echo "$R" | jq -r .progress)"
[ "$S" = "ready" ] && { echo "$R" | jq .; break; }
[ "$S" = "failed" ] && { echo "FAILED: $R"; exit 1; }
sleep 5
done
Turn a blog post URL into a talking-head avatar video — the avatar reads a summarized script of the post against a clean background. Use when the user wants...
---
name: revid-blog-to-avatar-video
description: Turn a blog post URL into a talking-head avatar video — the avatar reads a summarized script of the post against a clean background. Use when the user wants a personal/expert delivery vs an edited promo.
metadata: {"openclaw":{"requires":{"config":["REVID_API_KEY"]}}}
---
# Blog post → talking-head avatar video
Take any blog/article URL and produce a vertical (or square) talking-head video
with a chosen avatar reading a summarized version of the post.
## When to use this skill
- Source is a blog post / opinion piece / explainer with substantial body text.
- Output should feel like a *person delivering the take*, not an edited promo
with stock b-roll.
- An avatar (image URL or `characterId`) is available, or the user accepts the
default avatar.
- For an edited short with stock visuals, use
[`revid-article-to-short`](../revid-article-to-short/SKILL.md) instead.
## Inputs
| Field | Required | Notes |
|---|---|---|
| `url` | yes | Blog post URL |
| `avatar.url` *or* `characterIds[]` | yes | The face. Either an image URL or a saved consistent character ID (see [character mgmt](#consistent-characters)). |
| `aspectRatio` | no | Default `9:16`. Use `1:1` for LinkedIn. |
| `voiceId` | no | Match it to the avatar's tone if known. |
| `targetDuration` | no | Default 60 (s) — talking heads can run longer. |
## Step-by-step
1. Validate the URL.
2. If the user gave an avatar image URL, set `avatar.url`. If they gave a saved
character ID, set `characterIds: [id]` (and leave `avatar` omitted).
3. POST the payload below.
4. Poll `/status` (canonical loop in the Polling section below).
5. Return `videoUrl`.
## API call template
```http
POST /api/public/v3/render
Host: www.revid.ai
Content-Type: application/json
key: $REVID_API_KEY
```
```json
{
"workflow": "article-to-video",
"source": {
"url": "{BLOG_URL}",
"scrapingPrompt": "Extract the article body. Skip header, navigation, related posts, and footer."
},
"aspectRatio": "9:16",
"avatar": {
"enabled": true,
"url": "{AVATAR_IMAGE_URL}",
"removeBackground": true,
"imageModel": "good"
},
"voice": {
"enabled": true,
"voiceId": "aria-en-us",
"stability": 0.65,
"speed": 1.0,
"language": "en-US",
"enhanceAudio": true
},
"captions": { "enabled": true, "position": "bottom", "autoCrop": true },
"music": { "enabled": false },
"media": {
"type": "moving-image",
"density": "low",
"animation": "soft",
"placeAvatarInContext": true
},
"options": {
"targetDuration": 60,
"summarizationPreference": "summarize",
"hasToGenerateCover": true
},
"render": { "resolution": "1080p", "frameRate": 30 }
}
```
Notes:
- `placeAvatarInContext: true` composites the avatar over a relevant background
(vs a plain green-screen feel).
- `media.density: "low"` keeps cuts minimal so the talking head can carry the
video.
- `music.enabled: false` is the default — voice-driven content reads better
without competing audio.
## Consistent characters
If the user wants the **same face** across many posts, create a character once
and reuse the ID:
```bash
# 1. Create character
curl -s https://www.revid.ai/api/public/v3/consistent-characters \
-H "Content-Type: application/json" \
-H "key: $REVID_API_KEY" \
-d '{ "name": "Maya", "imageUrl": "https://cdn.example.com/maya.jpg" }'
# → { "id": "ch_…" }
# 2. Use it in renders
{ "characterIds": ["ch_…"], "avatar": { "enabled": true } }
```
List existing characters with `GET /api/public/v3/consistent-characters`.
## Examples
- [`examples/blog-to-avatar.json`](examples/blog-to-avatar.json) — payload.
- [`examples/run.sh`](examples/run.sh) — end-to-end curl flow.
## Polling
After `POST /render`, poll until `status === "ready"`:
```bash
PID="<pid-from-render>"
while :; do
R=$(curl -fsSL "https://www.revid.ai/api/public/v3/status?pid=$PID" \
-H "key: $REVID_API_KEY")
S=$(echo "$R" | jq -r .status)
case "$S" in
ready) echo "$R" | jq .; break ;;
failed) echo "FAILED: $R"; exit 1 ;;
*) sleep 5 ;;
esac
done
```
In production prefer setting `webhookUrl` in the request body and skip polling.
## Failure modes
| Symptom | Fix |
|---|---|
| Avatar lip-sync drifts on long copy | Lower `targetDuration` to 45 s, or switch `summarizationPreference: "summarize"` (already on). |
| Avatar background bleeds into video | Set `avatar.removeBackground: true` (default). For stubborn cases, pre-process the avatar image to a transparent PNG. |
| Background visuals distract from face | `media.density: "low"` and `media.animation: "soft"` (already on). For pure plain background, set `media.type: "custom"` + `media.useOnlyProvided: true` with a single neutral asset. |
| Voice doesn't match the avatar | Set `voice.voiceId` explicitly. The default voice is gendered female English — always override for other languages or personas. |
| `scrape failed` | Same as in [`revid-article-to-short`](../revid-article-to-short/SKILL.md): pre-scrape the post and switch to `script-to-video` with the avatar block intact. |
## See also
- [`revid-article-to-short`](../revid-article-to-short/SKILL.md) — same input,
edited-short output.
- [`revid-tweet-to-talking-head`](../revid-tweet-to-talking-head/SKILL.md) —
shorter form of the same talking-head pattern.
- [`revid-api-foundations`](../revid-api-foundations/SKILL.md).
FILE:examples/blog-to-avatar.json
{
"workflow": "article-to-video",
"source": {
"url": "https://blog.revid.ai/how-shorts-work",
"scrapingPrompt": "Extract the article body. Skip header, navigation, related posts, and footer."
},
"aspectRatio": "9:16",
"avatar": {
"enabled": true,
"url": "https://cdn.example.com/avatars/maya.jpg",
"removeBackground": true,
"imageModel": "good"
},
"voice": {
"enabled": true,
"voiceId": "aria-en-us",
"stability": 0.65,
"speed": 1.0,
"language": "en-US",
"enhanceAudio": true
},
"captions": { "enabled": true, "position": "bottom", "autoCrop": true },
"music": { "enabled": false },
"media": {
"type": "moving-image",
"density": "low",
"animation": "soft",
"placeAvatarInContext": true
},
"options": {
"targetDuration": 60,
"summarizationPreference": "summarize",
"hasToGenerateCover": true
},
"render": { "resolution": "1080p", "frameRate": 30 }
}
FILE:examples/run.sh
#!/usr/bin/env bash
# Blog post URL + avatar image -> talking-head video.
# Usage: REVID_API_KEY=… ./run.sh <blog-url> <avatar-image-url>
set -euo pipefail
: "?set REVID_API_KEY"
BLOG="?blog url required"
AVATAR="?avatar image url required"
HERE="$(cd "$(dirname "$0")" && pwd)"
PAYLOAD="$(jq --arg u "$BLOG" --arg a "$AVATAR" \
'.source.url=$u | .avatar.url=$a' \
"$HERE/blog-to-avatar.json")"
PID=$(curl -fsS https://www.revid.ai/api/public/v3/render \
-H "Content-Type: application/json" -H "key: $REVID_API_KEY" \
-d "$PAYLOAD" | jq -r .pid)
echo "pid=$PID"
while :; do
R=$(curl -fsSL "https://www.revid.ai/api/public/v3/status?pid=$PID" -H "key: $REVID_API_KEY")
S=$(echo "$R" | jq -r .status); echo " status=$S progress=$(echo "$R" | jq -r .progress)"
[ "$S" = "ready" ] && { echo "$R" | jq .; break; }
[ "$S" = "failed" ] && { echo "FAILED: $R"; exit 1; }
sleep 5
done
Turn a Shopify (or any e-commerce) product page URL into a 30–45 second 9:16 promo video ready for TikTok / Reels / Shorts. Use when the user shares a produc...
---
name: revid-shopify-product-promo
description: Turn a Shopify (or any e-commerce) product page URL into a 30–45 second 9:16 promo video ready for TikTok / Reels / Shorts. Use when the user shares a product link and wants a short ad/promo, not a long-form review.
metadata: {"openclaw":{"requires":{"config":["REVID_API_KEY"]}}}
---
# Shopify product → promo video
Take a product page URL and produce a vertical promo video that pulls the
product image(s), name, key features, and price.
## When to use this skill
- Input is a single product page URL from Shopify, WooCommerce, BigCommerce, or
any storefront with crawlable HTML (most stores).
- Output goal is a **promo / ad / launch teaser**, 30–45 s, vertical (9:16).
- The user wants Revid to extract the product details automatically. If they
hand you a script instead, use [`revid-script-to-video`](../revid-script-to-video/SKILL.md).
- For a generic ad written from a product description (no live URL), use
[`revid-product-description-to-ad`](../revid-product-description-to-ad/SKILL.md).
## Inputs
| Field | Required | Notes |
|---|---|---|
| `url` | yes | Public product page URL |
| `aspectRatio` | no | Defaults to `9:16` |
| `targetDuration` | no | Defaults to 35 (s) |
| `voiceId` | no | Default voice if omitted |
| `webhookUrl` | no | Skip polling if you can receive webhooks |
## Step-by-step
1. **Validate the URL** — must start with `http(s)://`. Reject obvious
non-product paths (`/cart`, `/blog`, `/collections/all`).
2. **Optional pre-flight** — fetch the URL once with `HEAD` to confirm it
returns 200. If 4xx, ask the user to confirm the link.
3. **Build the payload** (see template). Defaults are tuned for product promo:
high `density`, dynamic animation, captions ON, music ON.
4. **POST `/api/public/v3/render`** — capture the returned `pid`.
5. **Poll `/status?pid=…`** with the canonical loop (see Polling section
below) or wait for the webhook.
6. **Return** `{ pid, status, videoUrl, thumbnailUrl, durationSeconds, creditsUsed }`.
## API call template
```http
POST /api/public/v3/render
Host: www.revid.ai
Content-Type: application/json
key: $REVID_API_KEY
```
```json
{
"workflow": "article-to-video",
"source": {
"url": "{PRODUCT_URL}",
"scrapingPrompt": "Extract the product name, hero image, 3 key features, and price. Ignore reviews, related products, footer, and navigation."
},
"aspectRatio": "9:16",
"voice": { "enabled": true, "stability": 0.55, "speed": 1.05, "language": "en-US" },
"captions": { "enabled": true, "position": "middle", "autoCrop": true },
"music": { "enabled": true, "syncWith": "beats", "trackName": "uplifting-pop" },
"media": {
"type": "stock-video",
"density": "high",
"animation": "dynamic",
"quality": "pro",
"imageModel": "good",
"videoModel": "pro",
"turnImagesIntoVideos": true,
"applyStyleTransfer": false
},
"options": {
"targetDuration": 35,
"summarizationPreference": "summarize",
"hasToGenerateCover": true,
"coverTextType": "product-name",
"soundEffects": true,
"addStickers": false
},
"render": { "resolution": "1080p", "frameRate": 30 }
}
```
`scrapingPrompt` is the most important knob — it stops Revid from picking up
header/footer junk. Customize it per storefront if you find a recurring noise
pattern.
## Examples
- [`examples/shopify-aeropods.json`](examples/shopify-aeropods.json) — payload.
- [`examples/run.sh`](examples/run.sh) — end-to-end curl.
### Quick test
```bash
URL="https://your-shop.myshopify.com/products/your-product"
curl -s https://www.revid.ai/api/public/v3/render \
-H "Content-Type: application/json" \
-H "key: $REVID_API_KEY" \
-d "$(jq --arg url "$URL" '.source.url=$url' \
examples/shopify-aeropods.json)"
```
## Polling
After `POST /render`, poll until `status === "ready"`:
```bash
PID="<pid-from-render>"
while :; do
R=$(curl -fsSL "https://www.revid.ai/api/public/v3/status?pid=$PID" \
-H "key: $REVID_API_KEY")
S=$(echo "$R" | jq -r .status)
case "$S" in
ready) echo "$R" | jq .; break ;;
failed) echo "FAILED: $R"; exit 1 ;;
*) sleep 5 ;;
esac
done
```
In production prefer setting `webhookUrl` in the request body and skip polling.
## Failure modes
| Symptom | Fix |
|---|---|
| `scrape failed` / 403 from the URL | Storefront blocks bots. Open the page in a real browser, copy the title + 3 bullet features + price into a script, and switch to [`revid-script-to-video`](../revid-script-to-video/SKILL.md). |
| Video shows wrong product image | Storefront serves SSR via JS only. Pass `media.useOnlyProvided: true` and `media.provided: [{ url: "<hero-image-url>", type: "image" }]` to force the right asset. |
| Voice sounds robotic | Increase `voice.stability` to `0.7` and pick a specific `voice.voiceId`. Default voice varies. |
| Duration overshoots target | Set `options.summarizationPreference: "summarize"` (already in the template) and lower `targetDuration`. |
| Captions cover product | `captions.position: "top"` (or `"bottom"`). |
## See also
- [`revid-product-description-to-ad`](../revid-product-description-to-ad/SKILL.md)
if you don't have a live URL.
- [`revid-script-with-custom-media`](../revid-script-with-custom-media/SKILL.md)
if you want to control every visual.
- [`revid-api-foundations`](../revid-api-foundations/SKILL.md) for the contract.
FILE:examples/run.sh
#!/usr/bin/env bash
# End-to-end demo: Shopify product URL -> 9:16 promo video.
# Usage: REVID_API_KEY=sk_… ./run.sh https://your-shop.com/products/x
set -euo pipefail
: "?set REVID_API_KEY"
URL="-https://soundlabs.shop/products/aeropods-pro"
HERE="$(cd "$(dirname "$0")" && pwd)"
PAYLOAD="$(jq --arg url "$URL" '.source.url=$url' "$HERE/shopify-aeropods.json")"
echo "→ POST /render"
RESP=$(curl -fsS https://www.revid.ai/api/public/v3/render \
-H "Content-Type: application/json" \
-H "key: $REVID_API_KEY" \
-d "$PAYLOAD")
echo "$RESP" | jq .
PID=$(echo "$RESP" | jq -r .pid)
[ "$PID" != "null" ] || { echo "render failed: $RESP"; exit 1; }
echo "→ polling pid=$PID"
while :; do
STATUS=$(curl -fsSL "https://www.revid.ai/api/public/v3/status?pid=$PID" \
-H "key: $REVID_API_KEY")
S=$(echo "$STATUS" | jq -r .status)
P=$(echo "$STATUS" | jq -r .progress)
echo " status=$S progress=$P"
case "$S" in
ready) echo "$STATUS" | jq .; break ;;
failed) echo "FAILED: $STATUS"; exit 1 ;;
esac
sleep 5
done
FILE:examples/shopify-aeropods.json
{
"workflow": "article-to-video",
"source": {
"url": "https://soundlabs.shop/products/aeropods-pro",
"scrapingPrompt": "Extract the product name, hero image, 3 key features, and price. Ignore reviews, related products, footer, and navigation."
},
"aspectRatio": "9:16",
"voice": { "enabled": true, "stability": 0.55, "speed": 1.05, "language": "en-US" },
"captions": { "enabled": true, "position": "middle", "autoCrop": true },
"music": { "enabled": true, "syncWith": "beats", "trackName": "uplifting-pop" },
"media": {
"type": "stock-video",
"density": "high",
"animation": "dynamic",
"quality": "pro",
"imageModel": "good",
"videoModel": "pro",
"turnImagesIntoVideos": true,
"applyStyleTransfer": false
},
"options": {
"targetDuration": 35,
"summarizationPreference": "summarize",
"hasToGenerateCover": true,
"coverTextType": "product-name",
"soundEffects": true,
"addStickers": false
},
"render": { "resolution": "1080p", "frameRate": 30 }
}
Foundation knowledge for every Revid skill — auth, the single render endpoint, the workflow discriminator, polling, webhooks, and the response envelope. Load...
---
name: revid-api-foundations
description: Foundation knowledge for every Revid skill — auth, the single render endpoint, the workflow discriminator, polling, webhooks, and the response envelope. Load this once at session start; specific skills build on it.
metadata: {"openclaw":{"requires":{"config":["REVID_API_KEY"]}}}
---
# Revid API foundations
Everything every other skill in this library depends on. **Read this once.**
## When to use
Always — but transparently. Other skills assume the agent already knows:
- how to authenticate
- which endpoint to hit
- how to wait for the result
- the response envelope shape
## The shape of every Revid call
```
POST https://www.revid.ai/api/public/v3/render
Header: key: $REVID_API_KEY
Body: { "workflow": "<one-of-9>", "source": { … }, … }
↓
{ "success": 1, "pid": "p_…" }
↓
GET https://www.revid.ai/api/public/v3/status?pid=p_…
↓ (poll every 5–8 s)
{ "status": "ready", "videoUrl": "https://cdn.revid.ai/v/…mp4", … }
```
That's the entire contract. Skills only differ in the body of `POST /render`.
## Auth
```
key: $REVID_API_KEY
```
The header is literally named `key` (not `Authorization`). If unset, fail with a
clear message instead of calling the API.
## The 9 workflows
| Workflow | Use when input is… |
|---|---|
| `script-to-video` | Already-written script (text). |
| `prompt-to-video` | A one-liner idea — the API writes the script. |
| `article-to-video` | Any URL with text content (blog/product/news). |
| `avatar-to-video` | A script + an avatar image (talking-head). |
| `ad-generator` | A product description — AI writes ad hooks. |
| `music-to-video` | A music URL + visuals. |
| `motion-transfer` | A reference image animated with motion from a clip. |
| `caption-video` | An existing video that needs captions. |
| `static-background-video` | A voiceover over a fixed background. |
Pick the workflow that matches the *input shape*, not the *output you want*.
The same `script-to-video` workflow can produce a Reel, a YouTube short, or a
LinkedIn square — that's just `aspectRatio`.
## Source mapping
Each workflow expects one of these `source.*` fields:
| Workflow | Field |
|---|---|
| `script-to-video` | `source.text` |
| `prompt-to-video` | `source.prompt` (+ optional `source.stylePrompt`, `durationSeconds`) |
| `article-to-video` | `source.url` (+ optional `source.scrapingPrompt`) |
| `ad-generator` | `source.prompt` (the product description) |
| `avatar-to-video` | `source.text` (script) + top-level `avatar.url` |
| `music-to-video` / `caption-video` / `motion-transfer` | `source.url` |
| `static-background-video` | `source.text` + `media.backgroundVideo` |
If you put text in the wrong field, the call fails 422 with a schema error.
## Common knobs every skill should set
1. `aspectRatio` — pick from the consumer surface:
- Reels / TikTok / Shorts → `9:16`
- LinkedIn / Instagram feed → `1:1`
- YouTube long-form → `16:9`
2. `voice.enabled` — `true` for narrated content; `false` for music-only or
caption-only outputs.
3. `captions.enabled` — keep `true` by default. Most short-form watches happen
on mute.
4. `music.enabled` — `true` for promo / ad / story content; `false` for
talking-head where the avatar voice should breathe.
5. `render.resolution` — `1080p` is the right default. Use `720p` for cheap
previews; `4k` only when downstream needs it.
6. `media.quality` / `media.videoModel` — start at `pro`. Bump to `ultra` /
`veo3` / `sora2` only when the cost is justified. Mirror the `/render` body
to `POST /api/public/v3/calculate-credits` first to get a price estimate
without spending.
7. `webhookUrl` — pass it whenever the caller can receive webhooks. Saves polling
entirely.
## Reading the response
Success:
```json
{ "success": 1, "pid": "p_…", "workflow": "…", "endpoint": "…", "docs": {…} }
```
Failure:
```json
{ "success": 0, "error": "human-readable string" }
```
Skills should always check `success === 1` and fail loudly otherwise.
## Polling
After `POST /render`, poll until `status === "ready"`:
```bash
PID="<pid-from-render>"
while :; do
R=$(curl -fsSL "https://www.revid.ai/api/public/v3/status?pid=$PID" \
-H "key: $REVID_API_KEY")
S=$(echo "$R" | jq -r .status)
case "$S" in
ready) echo "$R" | jq .; break ;;
failed) echo "FAILED: $R"; exit 1 ;;
*) sleep 5 ;;
esac
done
```
Recommended cadence: poll every 5 s; back off to 8 s once `progress > 30`.
In production prefer setting `webhookUrl` in the request body and skip polling
entirely.
## Failure modes
The two most common errors every skill must handle:
- **`scrape failed` / `403` from source URL** — the page is JS-only or blocks
bots. Either pre-scrape it yourself and switch to `script-to-video`, or pass
`source.scrapingPrompt` with the manual title + body.
- **`insufficient_credits`** — drop `media.quality` / `videoModel` / `resolution`
and retry, or top up with `POST /api/public/v3/buy-credit-pack`.
## See also
- Full Revid Public API v3 spec: <https://documenter.getpostman.com/view/36975521/2sBXcGEfaB>
- The other Revid skills in this catalog apply this foundation to specific
content types (article, product page, blog, tweet, PDF, etc.).