MCP tools
Every RiffAds MCP tool, its arguments and what it returns.
Read as Markdown17 tools. A spend connection sees all 17. A read connection sees 12: the 5 spend tools are not registered. Setup: connect a client.
| Tool | Access | Limit per person | What it does |
|---|---|---|---|
riffads_ping | Read | none | Workspace, plan, whether this connection may spend |
list_capabilities | Read | 120/min | A page of what this workspace can generate |
get_capability_schema | Read | 120/min | One capability's JSON Schema and an example config |
estimate_generation | Read | 60/min | The number for max_credits. Reserves nothing |
list_actors | Read | 120/min | A page of actors |
list_voices | Read | 120/min | A page of voices |
get_credit_balance | Read | 120/min | Available credits and the agent spend ceiling in force |
submit_generation | Spend | 20/min | Starts one generation |
generate_talking_actor | Spend | 20/min | submit_generation for actor_ultra |
wait_for_generation | Read | 120/min | Blocks up to 20 seconds, answers when done |
get_generation | Read | 120/min | Reads one generation now |
get_batch | Read | 120/min | Reads every variant of one submit |
create_upload | Spend | 30/min | Reserves an ast_ id and a signed PUT URL |
finalize_upload | Spend | 30/min | Confirms the bytes landed, answers usable |
list_templates | Read | 120/min | Runnable workflow templates and their inputs |
run_workflow | Spend | 20/min | Runs a whole workflow |
get_workflow_run | Read | 120/min | Reads a run node by node |
Limits count per person, not per connection. Over a limit: rate_limited with retry_after_seconds.
Results and refusals
- Success: one text block of JSON with
ok: trueplus the tool's fields. Files also come asresource_linkblocks with signed URLs. - Refusal:
isErrorset. Body:ok: false,code,message,retryable,credits_charged. - Schemas are strict. Unknown keys are refused.
maxCreditsinstead ofmax_creditsis a refusal. capability_idis a free string, not an enum. New capabilities work with an old tool list.- Branch on
codeandretryable, never onmessage. Every code: errors.
{
"ok": false,
"code": "max_credits_exceeded",
"message": "This generation costs more than the max_credits you set (600 credits). This one needs 660 credits. Raise max_credits, or ask for something cheaper, such as a shorter or lower resolution render.",
"retryable": false,
"credits_charged": 0,
"limit": { "bound_by": "max_credits", "limit_credits": 600, "required_credits": 660 }
}limit.bound_by names the cap that refused: max_credits (yours to raise) or a workspace cap (ask an owner or admin).
Orient
riffads_ping
Read. No arguments. Spends nothing, no rate limit.
{
"ok": true,
"user_id": "...",
"organization_id": "org_...",
"workspace": "Northwind Studio",
"plan": "growth",
"mode": "spend",
"can_spend_credits": true,
"server_time": "2026-09-17T10:04:11.882Z"
}mode: "read" means no spend tools until a person changes it.
Discover
list_capabilities
Read. Paged.
| Argument | Type | Required | Notes |
|---|---|---|---|
limit | integer > 0 | no | Capped server side |
cursor | string | no | next_cursor from the previous page |
Returns capabilities[], total, next_cursor.
{
"ok": true,
"next_cursor": "gpt_image_2",
"capabilities": [
{
"capability_id": "actor_ultra",
"name": "Talking Actor",
"description": "OmniHuman 1.5: an actor speaks your script.",
"category": "avatar",
"output_kind": "video",
"status": "available"
}
]
}status:available,requires_plan,coming_soon,retired,unknown.requires_planrows carryrequired_plan.- Retired rows are left out of the list.
- Category order: avatar, video, image, preset, tool.
Every id: capabilities.
get_capability_schema
Read.
| Argument | Type | Required |
|---|---|---|
capability_id | string | yes |
Returns { capability: { capability_id, name, description, category, output_kind, config_schema, example_config } }.
config_schema: JSON Schema forconfig.example_config: the smallest config that validates.- Refusals may add
capability_statusandrequired_plan.
Some rules do not fit in JSON Schema
A schema-valid config can still be refused (some settings depend on other fields). Run estimate_generation to check a config before you submit.
Price
estimate_generation
Read. Reserves nothing, charges nothing.
| Argument | Type | Required |
|---|---|---|
capability_id | string | yes |
config | object | yes |
{
"ok": true,
"capability_id": "veo_31",
"credits": 400,
"is_ceiling": false,
"seconds": 8,
"breakdown": [
{ "label": "Video, 1080p with audio", "unit": "second", "quantity": 8, "credits": 400 }
]
}is_ceiling: true: an upper bound, not the exact number.secondsandnoteshow up only when they apply.- Numbers change. Estimate every time, do not cache.
Do not send the estimate back as max_credits
Send at least ceil(estimate * 1.1). The bare estimate is refused with max_credits_exceeded.
Library
list_actors
Read. Paged.
| Argument | Type | Required | Notes |
|---|---|---|---|
search | string | no | Matches names and descriptions |
limit | integer > 0 | no | Capped server side |
cursor | string | no | From the previous page |
Row: { actor_id, name, description, gender, age_band, tags[], default_voice_id, default_voice_name, is_platform }, plus next_cursor. is_platform is true for RiffAds actors, false for your workspace's own. No preview images.
list_voices
Read. Paged.
| Argument | Type | Required | Notes |
|---|---|---|---|
search | string | no | Matches voice names |
language | string | no | Exact language tag, for example en or es |
limit | integer > 0 | no | Capped server side |
cursor | string | no | From the previous page |
Row: { voice_id, name, language, gender, tags[], is_cloned, is_premium, is_platform }, plus total and next_cursor. No preview audio.
get_credit_balance
Read. No arguments.
{
"ok": true,
"credits_available": 4820,
"credits_on_hold": 660,
"agent_limits": {
"max_credits_per_generation": 600,
"daily_credits_limit": 2000,
"spent_last_24_hours": 740,
"daily_credits_remaining": 1260,
"most_one_generation_may_cost": 600,
"bound_by": "org_per_generation"
}
}most_one_generation_may_cost: the highestmax_creditsa submit can pass right now.bound_by:max_credits,org_per_generation,api_key_budgetororg_daily. API key fields never appear on MCP.
Spend
Spend tools are marked destructiveHint: true, so some clients ask before calling them.
submit_generation
Spend. Answers when the job is accepted, not when it is ready.
Prop
Type
{
"capability_id": "veo_31",
"config": { "prompt": "...", "duration": 8, "resolution": "1080p" },
"max_credits": 440
}{
"ok": true,
"generation_id": "gen_0193c8f0a1b24e7f9d3c5a6b7e8f0033",
"generation_ids": ["gen_0193c8f0a1b24e7f9d3c5a6b7e8f0033"],
"run_id": "run_9f2c1b",
"replay": false,
"capability_id": "veo_31",
"capability_name": "Veo 3.1",
"output_kind": "video",
"outputs_expected": 1,
"credits": { "credits_held": 440, "credits_charged": null, "settlement": "open", "terminal": false },
"charge_summary": "440 credits are on hold. Nothing has been charged so far.",
"next_action": "Call wait_for_generation with this generation_id..."
}generation_idsis always set.batch_group_id(bg_) appears only whenvariantsis above 1.run_id: nullis not a failure. Followgeneration_id.replay: true: the same request already ran. You get that generation back, no second charge.variantsabove the limit is refused, never clamped.voice_id(argument) is notvoiceId(insideconfig).- No output link on submit.
One agent submission at a time
A second submit while one is running gets submission_in_flight (retryable). Wait for the first, then send.
generate_talking_actor
Spend. Same as submit_generation with capability_id: "actor_ultra".
Prop
Type
Two generations, in this order
submit_generationwithcapability_id: "tts". Wait until it completes.generate_talking_actorwith thatgeneration_idasapproved_voice_generation_id.
Audio made for a different actor, voice or script is refused.
{
"capability_id": "tts",
"config": { "script": "Three weeks in and I am not going back." },
"actor_id": "act_4f1e...",
"voice_id": "voc_9a20...",
"max_credits": 40
}{
"script": "Three weeks in and I am not going back.",
"actor_id": "act_4f1e...",
"voice_id": "voc_9a20...",
"approved_voice_generation_id": "gen_1f88...",
"max_credits": 700
}- The
max_creditsvalues are placeholders. Estimate each step. scriptwins over ascriptinsidesettings.actor_ultrahas noaspect_ratio. Sending one is refused as an unknown key. Output matches the start frame's shape.- Result shape matches
submit_generation.
Full walkthrough: talking actor.
Follow
wait_for_generation
Read. The right way to follow a job.
| Argument | Type | Required |
|---|---|---|
generation_id | string | yes |
Blocks up to 20 seconds and answers the moment the job ends. still_running: true: call it again right away.
{
"ok": true,
"still_running": true,
"waited_seconds": 20,
"age_seconds": 74,
"retry_after_seconds": 0,
"generation": {
"generation_id": "gen_0193c8...",
"status": "rendering",
"outputs": []
},
"next_action": "Still working..."
}- Branch on
still_running, notstatus. age_secondsis the job's total age.- Pass a
progressTokento getnotifications/progressover an event stream.
get_generation
Read. One check, now. Do not loop it: use wait_for_generation.
| Argument | Type | Required |
|---|---|---|
generation_id | string | yes |
{
"ok": true,
"output_urls_expire_in_seconds": 600,
"generation": {
"generation_id": "gen_0193c8...",
"capability_id": "veo_31",
"status": "completed",
"batch_group_id": null,
"batch_index": null,
"outputs": [
{
"index": 0,
"kind": "video",
"url": "https://...signed...",
"file_name": "veo-31-0.mp4",
"width": 1080,
"height": 1920
}
],
"outputs_expected": 1,
"outputs_delivered": 1,
"credits": { "credits_held": 440, "credits_charged": 400, "settlement": "captured", "terminal": true },
"charge_summary": "400 credits were charged.",
"error": null,
"created_at": "2026-09-17T10:04:11.882Z",
"completed_at": "2026-09-17T10:09:52.104Z"
},
"next_action": "Done. The output links are short lived..."
}- Statuses:
queued,rendering,post_processing,completed,failed. A canceled job readsfailed. credits.credits_chargedisnulluntilcredits.terminalistrue. Null is unknown, not zero.- Each file also comes as a
resource_linkwithmimeTypeand a title likevideo 1 of 1.
Links die after 600 seconds
Download the file or hand the link on right away. Expired? Call get_generation again for a fresh link. See results.
get_batch
Read. Every variant of one submit.
| Argument | Type | Required |
|---|---|---|
batch_group_id | string | yes |
Returns status (running, completed, failed, partial), variants, variants_finished, outputs_delivered, one credits total, charge_summary, generations[], and a resource_link per delivered variant.
get_batch does not wait. Call wait_for_generation on a running id, then get_batch again. Batch credits_charged stays null until every variant ends.
Upload a file
Three steps. The middle one is your own HTTP call, not a tool.
create_upload
Spend. Reserves a slot. No bytes, no credits.
Prop
Type
| Kind | Types | Max |
|---|---|---|
| Image | image/jpeg, image/png, image/webp, image/gif | 20 MB |
| Video | video/mp4, video/webm, video/quicktime | 100 MB |
| Audio | audio/mpeg, audio/mp3, audio/wav, audio/x-wav, audio/webm, audio/ogg | 25 MB |
Returns asset_id, upload_url, upload_expires_at, upload_expires_in_seconds, kind, content_type, size_bytes, max_bytes_for_kind, content_checked, next_action.
PUT the bytes
One HTTP PUT to upload_url. Same content type, exact byte count, within 5 minutes.
finalize_upload
Spend. Call after the PUT returns.
| Argument | Type | Required |
|---|---|---|
asset_id | string | yes |
Returns { asset_id, scan_status, usable, kind, duration_ms, next_action }.
scan_status:pending,clean,flagged.- Use the
ast_id in a config only afterusable: true. usable: falsewithflaggedis final. Use another file.- Calling twice returns the same answer.
duration_msis measured server side. Null for images.
Only images are content checked. content_checked tells you. See content policy.
Workflows
Run a workflow built in the app, or a template. Agents cannot build or edit workflows. See workflows.
list_templates
Read. No arguments.
Returns templates[]. Each: { template_key, name, description, category, step_count, inputs[] }.
{
"node": "f_brand",
"label": "Brand context",
"field": "text",
"field_label": "value",
"kind": "text",
"max_length": 8000,
"filled": true
}kind:text,setting,actor,voice.settinginputs carryaccepts.textinputs carrymax_length.filled: true: the node already has a value.- Node ids cannot be guessed. Read them here.
run_workflow
Spend. Every step is charged.
Prop
Type
- Exactly one of
template_keyorworkflow_id. Both or neither:invalid_config. valueis a string, number or boolean. Fieldactortakes anact_id, fieldvoicetakes avoc_id.- All or nothing: one bad input refuses the whole run. The message names the node and field.
countcannot be set. Asset inputs cannot be filled.
{
"template_key": "ugc_face_cam",
"inputs": [
{ "node": "f_brand", "field": "text", "value": "A refillable cleaning spray sold direct." }
],
"max_credits": 1200
}{
"ok": true,
"workflow_run_id": "wfr_7c1e...",
"workflow_id": "wf_2ab9...",
"template_key": "ugc_face_cam",
"billable_node_count": 6,
"applied_inputs": [{ "node": "f_brand", "field": "text" }],
"replay": false,
"next_action": "This run has 6 step(s) to work through..."
}billable_node_count counts the steps that will run and charge. No output link on run_workflow.
get_workflow_run
Read. The only way to follow a run. There is no wait tool for runs.
| Argument | Type | Required |
|---|---|---|
workflow_run_id | string | yes |
Answers now. Call about every 30 seconds until still_running is false.
Returns workflow_run_id, workflow_id, status, still_running, nodes[], credits, started_at, finished_at, next_action, and output_urls_expire_in_seconds once a file is ready.
- Run status:
queued,running,completed,partial,failed,canceled. - Node:
{ node_id, label, status, credits, generations[], text, error }. Writer nodes returntext, no file. - Every file of every node comes as a
resource_link. credits.credits_chargedisnulluntil the run ends.credits_charged_so_fargrows while it runs.partial: some steps finished, failed steps charged nothing.
Not on MCP
- No cancel tool. A job canceled elsewhere reads
failed. - No outbound webhooks.
- No list of past generations. Keep your ids.
- No MCP resources or prompts.
- No workflow authoring.
- Nothing posts to Meta, TikTok, YouTube or X.
Id prefixes: statuses and ids.