Talking actor ads
Make the voice first, then the video that lip-syncs to it.
Read as MarkdownA talking actor ad is two generations, in order:
ttsturns your script into audio.actor_ultraanimates the actor to that audio. It needs the finished voice'sgeneration_id.
Have your own recording? Skip the voice. Jump down.
Pick an actor and a voice
List them with GET /actors and GET /voices (MCP: list_actors, list_voices). Actor ids start act_, voice ids voc_. If the actor has a default voice, you can leave out voice_id.
Make the voice, then the video
BASE=https://app.riffads.com/api/v1
AUTH="Authorization: Bearer $RIFFADS_API_KEY"
SCRIPT="Two weeks of battery, in a case this small."
# 1. The voice
VOICE=$(curl -s -X POST "$BASE/generations" -H "$AUTH" -H "Content-Type: application/json" -d "{
\"capability_id\": \"tts\",
\"config\": { \"script\": \"$SCRIPT\" },
\"actor_id\": \"act_0193c8f0a1b24e7f9d3c5a6b7e8f0011\",
\"voice_id\": \"voc_0193c8f0a1b24e7f9d3c5a6b7e8f0022\",
\"max_credits\": 10
}" | jq -r .generation_id)
# 2. Call until still_running is false and status is completed
curl -s "$BASE/generations/$VOICE/wait" -H "$AUTH"
# 3. The video, driven by that voice
curl -s -X POST "$BASE/generations" -H "$AUTH" -H "Content-Type: application/json" -d "{
\"capability_id\": \"actor_ultra\",
\"config\": { \"script\": \"$SCRIPT\" },
\"actor_id\": \"act_0193c8f0a1b24e7f9d3c5a6b7e8f0011\",
\"voice_id\": \"voc_0193c8f0a1b24e7f9d3c5a6b7e8f0022\",
\"approved_voice_generation_id\": \"$VOICE\",
\"max_credits\": 700
}"max_credits values above are placeholders. Get the real number from the estimate endpoint (estimate_generation) for each step, then send at least ceil(estimate * 1.1).
Both calls must match
Same script, same actor_id, same voice_id. Build the script once and reuse the variable. Any change: invalid_config, The approved voice no longer matches this generation.
What actor_ultra takes
| Field | Where | Notes |
|---|---|---|
actor_id or actor_image_asset_id | body | One is required. An uploaded image works as the face |
voice_id | body | Optional if the actor has a default voice. Config spells it voiceId |
approved_voice_generation_id | body | The completed tts generation |
script | config | Same words as the voice |
voice_audio | config | Your own recording instead of a voice |
- No
aspect_ratio. Refused. The video takes the actor image's shape. - No duration. Audio sets the length. It must be under 60 seconds.
- No
variants. Want 3 takes? Run the pair 3 times. - The id is
actor_ultra.talking_actoris refused.
Use your own audio
One generation. No tts, no script, no voice. Upload the file first and wait for usable: true.
{
"capability_id": "actor_ultra",
"config": { "voice_audio": ["ast_0193c8f0a1b24e7f9d3c5a6b7e8f0044"] },
"actor_id": "act_0193c8f0a1b24e7f9d3c5a6b7e8f0011",
"max_credits": 700
}Send it with submit_generation on MCP, not generate_talking_actor. If both a recording and a script arrive, the recording wins.
Common refusals
All are invalid_config, and nothing is charged.
| Message | Fix |
|---|---|
Generate and approve the voice before creating the video. | Make the tts step first |
The approved voice no longer matches this generation. | Send the same script, actor and voice |
Select a voice first. | Send voice_id |
Video sent while the voice still runs: submission_in_flight. Wait, then resend.
Getting the file: results.