API Documentation

Generate videos, static image ads, and downloadable audio (MP3) programmatically via REST API, or connect Wavemaker as an MCP tool in Cursor, Claude Desktop, or any MCP-compatible client.

Platform API & MCP (Comfy-native)

A Comfy-native product surface for chat-first workflows on Modal GPUs — Discover catalog, dual-meter runs (GPU + partner APIs), and a focused MCP tool surface over POST /mcp on the platform-api Worker. This is the live integration path. The managed video API (/api/v1/videos) and OAuth MCP at https://api.wavemaker.io/mcp are retired (HTTP 410).

Hosts (do not mix)

  • Managed mediahttps://api.wavemaker.io/api/v1/* + OAuth /mcp (sections below).
  • Platform Comfy — platform-api Worker ( $PLATFORM_API_URL): local http://127.0.0.1:8790; staging PUBLIC_PLATFORM_API_URL (workers.dev until custom host). Paths: /discover, /runs, Bearer POST /mcp.

Contracts

  • OpenAPI is the machine-readable source of truth (docs/platform/api/openapi.yaml); public GET /openapi.json on this host (no admin paths). The human reference (reference.md) and MCP tool inventory (mcp-reference.md) are generated from it and drift-gated in CI.
  • Deeper guides live under docs/platform/guides/api-mcp.md and docs/platform/billing-rates.md.
  • Product UI: /platform/marketing · App shell: /platform

REST happy path

  1. GET /discover or GET /discover/collections/{id}?resolve=1
  2. GET /workflows/{id} or GET /discover/workflows/{slug}apiGraph + promoted inputs
  3. POST /runs/preflight — GPU + partner line items
  4. POST /runs → poll GET /runs/{id}
  5. Outputs: GET /files/{id} + /content

CI executes this sequence as pnpm platform:quickstart (fixture mode by default; optional live test org with PLATFORM_QUICKSTART_LIVE=1 + $PLATFORM_API_URL / $PLATFORM_API_TOKEN). See docs/platform/guides/api-mcp.md § CI-executed REST quickstart.

curl -X POST $PLATFORM_API_URL/runs \
  -H "Authorization: Bearer <platform_jwt>" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{
    "workflowId": "<uuid>",
    "graph": { ... },
    "inputs": { "prompt": "editorial lookbook" },
    "gpuClass": "l40s"
  }'
curl -X POST $PLATFORM_API_URL/runs/preflight \
  -H "Authorization: Bearer <platform_jwt>" \
  -H "Content-Type: application/json" \
  -d '{ "graph": { ... }, "gpuClass": "l40s" }'
curl "$PLATFORM_API_URL/discover?q=fashion&limit=5" \
  -H "Authorization: Bearer <platform_jwt>"

Platform MCP (POST $PLATFORM_API_URL/mcp)

JSON-RPC 2.0 over HTTPS with a Bearer org JWT against the platform-api host — never the managed OAuth URL. Session-bound tools accept sessionId in arguments or X-Platform-Session-Id. Methods: initialize, tools/list, tools/call.

curl -X POST $PLATFORM_API_URL/mcp \
  -H "Authorization: Bearer <platform_jwt>" \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "id": 1,
    "method": "tools/list"
  }'

Connect your client

Mint a wm_ API key in the app (Account → Keys), then paste one of these. Unlike raw generation APIs, every deliverable your agent lists here already passed the platform's QC gates — you browse reviewed deliverables, not raw generations.

Claude Code
claude mcp add wavemaker-platform \
  --transport http $PLATFORM_API_URL/mcp \
  --header "Authorization: Bearer wm_<your_api_key>"
Claude Desktop (claude_desktop_config.json)
{
  "mcpServers": {
    "wavemaker-platform": {
      "url": "$PLATFORM_API_URL/mcp",
      "headers": { "Authorization": "Bearer wm_<your_api_key>" }
    }
  }
}
Cursor
// .cursor/mcp.json
{
  "mcpServers": {
    "wavemaker-platform": {
      "url": "$PLATFORM_API_URL/mcp",
      "headers": { "Authorization": "Bearer wm_<your_api_key>" }
    }
  }
}
ChatGPT (developer mode)
ChatGPT → Settings → Connectors → Developer mode →
Add server: $PLATFORM_API_URL/mcp
Auth: custom header — Authorization: Bearer wm_<your_api_key>
Tool Purpose
search_workflows Hybrid catalog search
get_workflow Detail by workflowId or slug
apply_workflow Load graph into a session
set_promoted_inputs Fill guided-run inputs
edit_graph Typed graph ops (never raw JSON patch)
compose_workflows Multi-workflow splice plan
run_workflow Cost preflight + submit
get_run / list_runs / cancel_run Run lifecycle
list_run_groups / get_run_group Segmented progress (directed video, long-form film)
list_creations Browse reviewed deliverables (succeeded runs + files)
list_flows / run_flow Org-saved Flows — one-call re-runs of proven settings
generate Prompt → top catalog hit (selection)
list_models / list_partner_nodes Registry + Wavemaker* nodes
list_collections / get_collection Curated Discover sets
get_file / get_wallet Outputs + credit balances
get_session / list_sessions Compact graph index (no full SessionDO dump)
iterate_resolve / iterate_execute Gallery verbs; face_swap needs likenessConsent assertion
remix_workflow / save_flow Remix a public workflow; save a version-pinned Flow
list_creative_packs / engage_creative_pack Creative packs (toolset=packs)
engage_campaign / get_campaign DCO: feed × template, 24-variant cap (toolset=campaigns)
reassemble_run_group / adopt_run_group_candidate Reorder/concat a terminal group; adopt a QC candidate
mint_file_upload / get_generation_details Presigned PUT + curl; library provenance

Billing (dual meter)

Reserve = GPU estimate + partner call plan × headroom; settle = actual GPU-seconds + priced partner ledger lines from the runner. generate (selection) is free; run_workflow / POST /runs bill. Details in docs/platform/billing-rates.md and on the pricing page.

Catalog Video (DCO)

Feed file × template → up to 24 variants on one dco-campaign FanOut × SubWorkflow (campaignId = runGroupId). Dashboard: /platform/campaigns (status + download). Always mounted — no dcoEnabled kill. Retired /api/v1/dco/* returns HTTP 410. Help: Catalog Video docs.

POST $PLATFORM_API_URL/campaigns/engage Bearer wm_ · runs:write

Body: feedFileId, template (baseSlug static-ads | ads-catalog-video, prompt with {{title}} slots, optional formatIds / aspectRatio / styleId), optional rowIds / sessionId. Feed validation + image ingest run before reserve. Over 24 variants → 422 campaign_variant_cap_exceeded. MCP twin: engage_campaign.

GET $PLATFORM_API_URL/campaigns Bearer wm_ · runs:read

Org campaign list (dashboard). Not an MCP tool — agents use get_campaign after engage.

GET $PLATFORM_API_URL/campaigns/{campaignId} Bearer wm_ · runs:read

Run-group projection plus SKU-keyed variant statuses/outputs. MCP: get_campaign. Files via GET /files/{id}.

Managed media API — retired. Everything below this notice documents the previous-generation managed surface (api.wavemaker.io /api/v1/* + OAuth /mcp). These endpoints have been retired and now return HTTP 410 Gone — the reference below is kept for historical context only. Build on the Platform API & MCP above ($PLATFORM_API_URL origin-rooted paths, key prefix wm_, tools-only MCP at /mcp), which is the only supported integration surface.

Authentication

All REST endpoints require an API key passed as a Bearer token. Create and manage keys from your dashboard API Keys page. The MCP endpoint at /mcp uses OAuth 2.1 instead — see MCP Integration.

API Key Format

Keys are prefixed with mcp_ and are at least 32 characters long. Store them securely — they cannot be retrieved after creation. Only the last 4 characters remain visible in the dashboard (e.g. mcp_...a1b2). API key creation requires a paid plan (Pro or higher) with api_access.

Rate Limits

30 requests per minute per API key on /api/v1/* and /mcp (rolling 60-second window). Rate-limited responses include X-RateLimit-Remaining and X-RateLimit-Reset (Unix seconds); 429 adds a Retry-After header. /assets/sign is auth-protected but not rate-limited.

Authorization: Bearer mcp_your_api_key_here

Bind an API key to an organization

Every generation / refinement / workspace-tool / MCP generate dispatch requires a billing organization. A key with neither user_id nor organization_id is rejected before dispatch with 402 org_required. New keys minted from Settings → API keys while an org is active carry both. User-scoped keys without organization_id resolve the user's default org at dispatch; if that is also missing you get the same 402 with code: "NO_BILLING_ORG" — sign in once to initialize the workspace. Fully unscoped ops keys must be re-issued under an org by an admin.

Quick Start (legacy — retired)

These /api/v1 calls now return HTTP 410 Gone. For a working quickstart, submit a run against the Platform API (POST $PLATFORM_API_URL/runs with a wm_ key, then poll GET /runs/{runId}). The three steps below are retained only to show the shape of the retired managed flow.

1. Create a video generation job
curl -X POST https://api.wavemaker.io/api/v1/videos \
  -H "Authorization: Bearer mcp_your_api_key" \
  -H "Content-Type: application/json" \
  -d '{
    "prompt": "Create a 30-second ad for a coffee shop in Brooklyn",
    "aspect_ratio": "16:9",
    "duration": 30
  }'
2. Poll for status
curl https://api.wavemaker.io/api/v1/videos/wf_abc123def456 \
  -H "Authorization: Bearer mcp_your_api_key"
3. Get the composition
curl https://api.wavemaker.io/api/v1/videos/wf_abc123def456/composition \
  -H "Authorization: Bearer mcp_your_api_key"

Or use webhooks to get notified automatically when the video is ready.

Videos

POST /api/v1/videos API Key

Start a new video generation job. The AI analyzes your prompt, plans scenes, generates visuals, voiceover, and music, then composes a final video. Costs credits based on complexity.

Request Body
ParameterTypeRequiredDescription
prompt string Required Description of the video to generate. Min 3 characters. May include a URL to scrape brand assets and content from.
aspect_ratio string Optional 16:9 (default), 9:16, 1:1, 4:5, or 2:3
duration number Optional Target duration in seconds (e.g. 30, 60). Drives credit reserve and is injected into the planner's system prompt via a non-negotiable duration directive so the final composition lands within ±1 s of the target.
platform string Optional Target platform hint for the planner (e.g. youtube, instagram, tiktok, tv_ctv). Influences pacing, format, and production elements.
style string Optional Visual style preference passed to the planner (e.g. cinematic, playful animated 2D).
webhook_url string Optional HTTPS URL to receive a POST when the job completes, is cancelled, or fails. See Webhooks. Private IPs, credentials in the URL, and non-HTTPS schemes are rejected with 400.
webhook_secret string Optional Secret used to sign every webhook delivery for this job via HMAC-SHA256. The receiver verifies the X-Webhook-Signature: v1,<hex-hmac> header against <X-Webhook-ID>.<X-Webhook-Timestamp>.<raw body>.
Response

202 Job accepted (Retry-After: 10)

{
  "job_id": "wf_abc123def456789",
  "status": "running",
  "message": "Video generation started. Poll GET /api/v1/videos/{job_id} for status, or stream GET /api/v1/videos/{job_id}/events for real-time progress.",
  "webhook_url": "https://your-server.com/webhook"
}

job_id is prefixed with wf_ followed by a 32-character hex string. Keep it; you'll need it for all subsequent calls.

Error Responses

400 Invalid JSON, missing/short prompt, unknown aspect_ratio, or invalid webhook_url

402 Insufficient credits, CREDITS_OWED from a prior settlement upcharge, or NO_BILLING_ORG (the key's account has no organization yet — sign in to the dashboard once to initialize it). Response includes code and details.

429 Rate limit or monthly quota exceeded (Retry-After on rate limit only)

503 Inngest dispatch unavailable (Retry-After: 60)

GET /api/v1/videos/:jobId API Key

Check the status and progress of a video generation job. Returns an ETag header for conditional requests, and Retry-After while the job is still running (3s when progress_pct > 70, 8s otherwise).

Response

200 Job status

{
  "job_id": "wf_abc123def456789",
  "status": "running",
  "phase": "generate_video_clip",
  "progress_pct": 65,
  "composition_id": "comp_xyz"
}

composition_id, output, and error are only present when populated. phase is the name of the most recently-started tool or phase event (e.g. generate_image, compose_scenes).

Status Values
StatusDescription
runningAccepted or actively generating. Check phase and progress_pct.
completeDone. composition_id and output are populated.
erroredFailed. See error field. May still expose a partial composition_id if emergency-save fired.
GET /api/v1/videos/:jobId/events API Key

Stream real-time progress as Server-Sent Events. The stream emits one event per workflow event (with the workflow's event_type as the SSE event: field) plus periodic : keepalive comments, and terminates with a final complete, errored, or timeout event. Maximum stream duration is 30 minutes; reconnect with ?last_event_id= to resume.

Event types you'll see
EventDescription
phaseWorkflow phase label (e.g. planning, generating media)
tool_startA tool started. detail.sceneId is set for per-scene work.
tool_completeA tool finished. detail.assetType = image / video / voiceover / music.
tool_errorA tool failed. message contains the user-facing reason.
thinkingOrchestrator reasoning chunk (when extended-thinking is on).
node_linkLineage link between a parent tool call and its media child (used by the canvas UI).
completeFinal event — job succeeded. data.composition_id is the ID to fetch.
erroredFinal event — job failed. data.error is the reason.
Query Parameters
ParameterTypeDescription
last_event_id integer Resume after this event ID (use the numeric id: from a previous SSE frame).
Response

SSE text/event-stream

id: 12
event: phase
data: {"id":12,"event_type":"phase","message":"Generating media","created_at":"2026-04-01T12:01:00Z","progress":35}

id: 13
event: tool_start
data: {"id":13,"event_type":"tool_start","tool_name":"generate_image","message":"generate_image","detail":{"sceneId":"scene-3"},"progress":40}

id: 14
event: tool_complete
data: {"id":14,"event_type":"tool_complete","tool_name":"generate_image","detail":{"sceneId":"scene-3","assetType":"image"},"progress":52}

: keepalive

event: complete
data: {"status":"complete","composition_id":"comp_xyz","output":{"compositionId":"comp_xyz"}}
GET /api/v1/videos/:id/composition API Key

Retrieve the full video composition JSON. :id accepts either the wf_* job ID returned by POST /api/v1/videos or a composition_id (UUID) from a previous status response. Contains scene definitions, style spec, audio tracks, and R2 asset keys (resolve via POST /assets/sign).

Response

200 Composition data

{
  "composition_id": "comp_xyz",
  "composition": {
    "id": "comp_xyz",
    "title": "...",
    "aspectRatio": "16:9",
    "totalDurationFrames": 900,
    "fps": 30,
    "scenes": [...],
    "style": {
      "colorPalette": {...},
      "typography": {...},
      "motionLanguage": "cinematic",
      "visualMedium": "photorealistic"
    },
    "audio": {
      "voiceover": {...},
      "music": {...}
    }
  }
}
POST /assets/sign API Key

Generate signed public URLs for composition asset keys. Use this to resolve asset references from the composition JSON into downloadable URLs. Up to 50 keys per request. For each image key, the response also includes _400w.jpg and _800w.jpg variants when available; for each video key, the _web.mp4 and _preview.mp4 variants. Keys that don't exist in storage are silently omitted from the response (no 404).

Request Body
{
  "keys": ["assets/scene-1/image.png", "assets/scene-2/video.mp4"]
}
Response

200

{
  "urls": {
    "assets/scene-1/image.png": "https://api.wavemaker.io/assets/...",
    "assets/scene-1/image_400w.jpg": "https://api.wavemaker.io/assets/...",
    "assets/scene-1/image_800w.jpg": "https://api.wavemaker.io/assets/...",
    "assets/scene-2/video.mp4": "https://api.wavemaker.io/assets/...",
    "assets/scene-2/video_web.mp4": "https://api.wavemaker.io/assets/...",
    "assets/scene-2/video_preview.mp4": "https://api.wavemaker.io/assets/..."
  }
}

Static Image Ads

Generate platform-ready static image ads — or plain AI images. The default purpose: "ad" plans the headline, CTA, and visual concept from your brief (or a site URL's scraped brand facts), renders the ad with baked copy, quality-checks it (auto-fixing failures once), and delivers the exact platform pixel size. purpose: "image" renders your prompt as-is at the requested size — no ad copy, branding, or advertising rules (a plain-image quality check still runs: prompt adherence + technical defects, one auto-fix). Adult prompts are supported for purpose: "image" on entitled accounts (paid plan + adult content enabled in the profile) — they auto-route to the adult engines and the image_model pin is ignored; the ad purpose is standard-content only. One deliverable per call — make one call per ad/image you want, or set carousel_cards for a narrative carousel. The same run also works through the workspace tool rails (POST /api/v1/workspaces/:id/tools/generate_static_ad) and the MCP generate_static_ad tool.

POST /api/v1/static-ads API Key

Dispatch one static ad generation (or an N-card carousel). Charges a flat 8 credits per ad (carousel: 8 × cards). Async: poll the status URL, or pass webhook_url for a signed tool_run.completed push.

Request Body
ParameterTypeRequiredDescription
prompt string Required* The creative brief. *Required unless site_url is set.
site_url string Required* Brand site URL — scraped for real brand facts (name, colors, logo). *Required unless prompt is set.
purpose string Optional ad (default — planned copy, branding, ad QC) or image (plain image — the prompt renders as-is, no ad framing).
image_model string Optional Image model pin: gemini-image (Nano Banana, default), seedream (Seedream 4.0), or xai (Grok Imagine).
platform string Optional meta, google, reddit, linkedin, x, tiktok, pinterest, or universal (default). Drives per-platform compliance rules and defaults. Ad purpose only.
format string Optional Ad format id from GET /api/v1/ad-formats (e.g. square, story_916, landscape_191). Default: resolved from aspect_ratio, else square.
aspect_ratio string Optional Alternative to format: a ratio like "1:1", "9:16", "1.91:1".
style_id string Optional Pin a visual style id from GET /api/v1/image-styles (GET /api/v1/ad-styles is a deprecated alias); omit for AI selection. Must be an "ad"-capable style when purpose is "ad", or a "plain"-capable style when purpose is "image".
focus_hint string Optional Extra creative direction (high priority).
prompt_mode string Optional styled (default) uses the style-template planner; free_form skips it.
carousel_cards number Optional 2–6: generate a narrative carousel of N same-format cards (hook → problem → solution → proof → benefit → offer) instead of one ad. Priced per card.
webhook_url string Optional HTTPS URL for a signed tool_run.completed / tool_run.failed POST on terminal status. See Webhooks.
webhook_secret string Optional Secret for the HMAC-SHA256 X-Webhook-Signature header (same scheme as video webhooks).
Example
curl -X POST https://api.wavemaker.io/api/v1/static-ads \
  -H "Authorization: Bearer mcp_your_api_key" \
  -H "Content-Type: application/json" \
  -d '{
    "prompt": "Bold launch ad for our espresso subscription",
    "site_url": "https://example-coffee.com",
    "platform": "meta",
    "format": "square"
  }'
Response

202 Run accepted

{
  "run_id": "wtr_abc123def456",
  "credits_charged": 8,
  "status": "pending",
  "status_url": "/api/v1/static-ads/wtr_abc123def456"
}
Error Responses

400 Invalid JSON or input (neither prompt nor site_url)

402 Insufficient credits

503 Dispatch infrastructure temporarily unreachable

GET /api/v1/static-ads/:run_id API Key

Poll a static-ad run. While in flight the response is { run_id, status } (pending / running). A failed run returns 502 with status: "error" and an error reason. On completion each delivered variant carries the final image URL, exact pixel size, ad copy, and its QC report. Single-ad runs also surface the one variant as variant; carousels return all cards in variants.

Response (complete)
{
  "run_id": "wtr_abc123def456",
  "status": "complete",
  "variant": {
    "id": "adv_123",
    "url": "https://api.wavemaker.io/assets/...",
    "width": 1080,
    "height": 1080,
    "aspect_ratio": "1:1",
    "format": "square",
    "platforms": ["meta"],
    "headline": "Espresso, Delivered.",
    "cta": "Start Your Trial",
    "qc": { "status": "passed", "issues": [] }
  },
  "variants": [ ... ]
}
GET /api/v1/ad-formats API Key

Free catalog of ad formats and platform packs: each format's exact delivery pixels, ratio, and the platforms it serves, plus each platform's recommended format pack. Use a format id (or a platform) in POST /api/v1/static-ads.

GET /api/v1/ad-styles API Key

Deprecated — superseded by GET /api/v1/image-styles below (the response carries a Link: rel="successor-version" header). Free catalog of ad-capable visual styles only (dark authority, testimonial spotlight, minimalist hero, meme format, ...): id, name, use-case guidance, and platform compatibility. Pass a style id as style_id to pin the look; omit for AI selection. Both endpoints project off the same catalog, so a caller mid-migration never sees drift between the two responses.

GET /api/v1/image-styles API Key

Free catalog of every Image Style (~110 seeded looks) — the successor to /api/v1/ad-styles. Each row is { id, name, category, kind, applicable_purposes, use_case_guidance, platform_compatibility }. applicable_purposes governs where a style_id is valid: an "ad"-capable style (kind: "ad_layout") pins style_id on POST /api/v1/static-ads when purpose is the default "ad"; a "plain"-capable style (kind: "look") pins style_id when purpose: "image" (no ad copy). A style may declare both purposes. The MCP equivalent is the list_image_styles tool.

POST /api/v1/static-ads/variants/:variant_id/actions API Key

Edit a delivered image (the site's iteration loop). Body: action (edit / regenerate / change_style / adapt_ratio), instruction (required for edit), style_id (for change_style — from GET /api/v1/image-styles), target_format_id (for adapt_ratio — from /ad-formats), optional webhooks. Credits: edit 3; others 5. Versions are append-only. MCP: edit_static_ad. Returns 202 with run_id, credits_charged, and status_url.

POST /api/v1/static-ads/sets/:set_id/actions API Key

Set-level edit (action defaults to set_edit): apply one instruction across every non-approved image of the set (≤12 images; 5 credits × image). Returns 202 with run_id, credits_charged, and status_url.

Creative Packs

One brief → a coordinated multi-master deliverable. Shared brand-identity prep runs once, then every master gets a full independent Plan+Produce run. Kinds: multi_ar_pmax (4 aspect ratios for Performance Max / Demand Gen), video_ad_sequence (YouTube bumper 6s + non-skippable 15s + CTV 30s), hook_variant (one 15s body + 3–8 swapped 3s hooks), and photo_set (10–20 stills + ZIP; standard content only). MCP: creative_pack_generate / creative_pack_status. Kill switch creativePacksEnabled (503 when OFF). Full reference: docs/public-api.md § Creative Packs.

POST /api/v1/creative-packs API Key

Dispatch one pack. Idempotency-Key header required (unique per org) — identical body replay returns the original run (200, Idempotent-Replayed); a different body under a reused key 409s. Body: kind, brief (required), hook_count (hook_variant, 3–8, default 5), still_count / style_id (photo_set), webhook_url. Whole-pack credits held up front and settled per-master. photo_set 403s NSFW-entitled callers toward generate_static_ad.

Example Request
curl -X POST https://api.wavemaker.io/api/v1/creative-packs \
  -H "Authorization: Bearer mcp_your_api_key" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: pack-order-8812" \
  -d '{
    "kind": "multi_ar_pmax",
    "brief": "Performance Max pack for https://acme.com — free shipping CTA"
  }'
Example Response (202)
{
  "pack_run": {
    "pack_run_id": "cpr_…",
    "kind": "multi_ar_pmax",
    "status": "pending",
    "credits_held": 120,
    "master_count": 4,
    "status_url": "/api/v1/creative-packs/cpr_…"
  }
}
GET /api/v1/creative-packs API Key

Free catalog of the 4 pack kinds with their masters (role, aspect ratio, treatment id, duration / still count).

GET /api/v1/creative-packs/:id API Key

Poll. Overall status (pending / running / succeeded / partial / failed / cancelled; credits_held / credits_settled; zip_url when a photo_set ZIP is built; each master's own status + result_urls.

DELETE /api/v1/creative-packs/:id API Key

Cancel an in-flight pack. Cancels child kernel runs, refunds unused hold (completed masters keep their reserved share), marks the pack cancelled. No-op once terminal.

Audio

First-class downloadable MP3 sets — not video soundtrack stems. Four purposes: music, voice, spot (radio/podcast ads with duration packs :06/:15/:30/:60), and podcast (topic episodes, 3–15 min, up to 2 hosts). Same rails as chat Audio mode. Also callable through workspace tools (POST /api/v1/workspaces/:id/tools/generate_audio / edit_audio_set) and MCP. Kill switch audioOutputModeEnabled — when OFF, generate/edit return 503 audio_output_mode_disabled; style/treatment catalogs stay available. Full operator reference: Audio generation (site guide) / docs/audio-generation.md.

POST /api/v1/audio API Key

Dispatch one audio set. Credits are reserved from purpose × duration × variants (see credits_charged). Async: poll the status URL, or pass webhook_url for a signed tool_run.completed push.

Request Body
ParameterTypeRequiredDescription
prompt string Required* Creative brief. *Required unless brief is set.
brief string Optional Alias for prompt.
purpose string Optional music (default), voice, spot, or podcast.
duration_ids string[] Optional Spot only: spot_6 / spot_15 / spot_30 / spot_60 (or bare 6 / 15 / …). Default spot_30.
variants_per_duration number Optional A/B takes per Spot duration or Music take (1–3, default 1).
script_verbatim boolean Optional When true, spoken words must not be rewritten (Voice/Spot/Podcast).
script_text string Optional Spoken script / dialogue text.
site_url string Optional Spot brand URL — grounded for name/CTA facts (no image refs).
target_duration_ms number Optional Music / Voice / Podcast target length in ms.
speakers object[] Optional Up to 2 speakers: { id, name, voice_id?, voice_description? }.
style_id string Optional Pin from GET /api/v1/audio-styles / list_audio_styles.
treatment_id string Optional Pin from GET /api/v1/audio-treatments / list_audio_treatments.
force_instrumental boolean Optional Music: force instrumental (no vocals).
webhook_url string Optional Signed tool_run.completed / tool_run.failed delivery.
webhook_secret string Optional HMAC secret for webhook verification.
Example
curl -X POST https://api.wavemaker.io/api/v1/audio \
  -H "Authorization: Bearer mcp_your_api_key" \
  -H "Content-Type: application/json" \
  -d '{
    "prompt": "30-second radio ad for https://acme.com — free shipping CTA",
    "purpose": "spot",
    "duration_ids": ["spot_15", "spot_30"],
    "variants_per_duration": 2
  }'
Response

202

{
  "run_id": "wtr_audio123",
  "credits_charged": 39,
  "status": "pending",
  "status_url": "/api/v1/audio/wtr_audio123"
}
GET /api/v1/audio/:id API Key

Poll a generate or edit run. In flight: { run_id, status }. Complete: audio_set_id + variants[] (each with url, mime_type: "audio/mpeg", duration, QC). Failed: 502.

Complete response
{
  "run_id": "wtr_audio123",
  "status": "complete",
  "audio_set_id": "auset_…",
  "variants": [
    {
      "id": "auvar_…",
      "url": "https://…/spot-15-a.mp3",
      "mime_type": "audio/mpeg",
      "duration_ms": 15000,
      "duration_id": "spot_15",
      "purpose": "spot",
      "variant_index": 0,
      "qc": { "status": "passed" }
    }
  ]
}
POST /api/v1/audio/sets/:set_id/actions API Key

Edit a delivered set via edit_audio_set. Actions: regenerate (default), change_style (requires style_id), change_voice, rewrite_script, remix_bed, more_variants. Also POST /api/v1/audio/variants/:variant_id/actions (path supplies the variant). Distinct from video soundtrack tool edit_audio.

Example
curl -X POST https://api.wavemaker.io/api/v1/audio/sets/auset_…/actions \
  -H "Authorization: Bearer mcp_your_api_key" \
  -H "Content-Type: application/json" \
  -d '{
    "action": "change_style",
    "style_id": "aus_lofi",
    "instruction": "softer bed, keep the VO"
  }'
GET /api/v1/audio-styles API Key

Free catalog of aus_* styles. Optional ?purpose=music|voice|spot|podcast. Available even when the Audio kill switch is OFF. Sibling: GET /api/v1/audio-treatments.

Retired /api/v1/dco/* (sources, produce, manifest, VAST) returns HTTP 410. Live DCO is Catalog Video on platform-api (POST /campaigns/engage).

Video Upscaling

Super-resolve any external video to a higher resolution with audio preserved, via the upscale_video tool on the workspace tool-run surface. Point it at a public HTTPS URL of any duration — the system internally segments, upscales, concatenates, and remuxes, so you make ONE call regardless of length. Async: poll the run or set a webhook_url. (Part of the retired managed API — see the Platform API for the supported equivalent.)

POST /api/v1/workspaces/:id/tools/upscale_video API Key

Create a workspace once (POST /api/v1/workspaces), then dispatch this tool. Returns a run_id + the dynamically-computed credits_charged; poll GET /api/v1/tool-runs/:run_id for result.output.download_url.

Input (inside { "wait": false, "input": { … } })
ParameterTypeRequiredDescription
video_url string Required Public HTTPS URL of the source video (any duration; SSRF-validated). Max 2 h / 5 GB.
target_short_side number Optional Output short side in px, 240–2160 (default 1080). Aspect ratio preserved.
preserve_audio boolean Optional Mux the original audio into the output (default true).
quality string Optional standard (default, Real-ESRGAN, cheapest), pro (Topaz, pro-grade, standard content only), or premium (SeedVR2, best for heavily degraded sources).
Pricing (credits per output-minute + per-job base)
quality≤720≤1080≤2160 (4K)+ base
standard81770+3
pro4545140+4
premium3270220+5
Response

202 { "run_id": "wtr_…", "status": "pending", "credits_charged": N }

Error Responses

402 Insufficient credits

413 Source too long (>2 h) / too large (>5 GB)

422 Undecodable / undeterminable-duration media

Workflows as Endpoints

Every published workflow — yours or one from Discover — is a REST endpoint (POST /runs) and an MCP tool. Retired POST /w/{slug}/runs returns 410. Product landing: /workflow-api. Platform API & MCP section above.

POST /api/v1/workflows/:id/runs API Key

Submit a run. Get an itemized quote first with POST /api/v1/workflows/:id/estimate (per-node credits, BYOK fees, and any creator premium). Poll GET /api/v1/kernel-runs/:run_id for per-node progress, costs, and artifact ids.

Request Body
Idempotency

Send an Idempotency-Key header (≤255 chars, permanent per organization) to make submits retry-safe: a replay returns the original run with idempotent_replayed: true and the Idempotent-Replayed: true header — exactly one credit hold no matter how many times you retry.

Response

202 { "run_id": "…", "status": "queued", "credits_held": N }

Related Endpoints

POST /api/v1/kernel-runs/:id/cancel — cancel; keeps actual node spend, refunds the rest

POST /api/v1/kernel-runs/:id/approval — decide a pending approval gate

GET /api/v1/kernel-artifacts/:id — artifact metadata + download URL

POST /api/v1/provider-credentials — BYOK vault (write-only; 20% platform fee, min 1 credit, on BYOK steps)

GET /api/v1/hub/workflows?q=… — search the public Hub catalog

GET /api/v1/w/:slug — run a workflow as a product by its Hub slug: detail + typed inputs as JSON Schema (Draft 2020-12), then POST /api/v1/w/:slug/runs or /estimate (same gates and billing as the id routes)

POST /api/v1/workflows/compile — stateless compile: validate a raw WorkflowSpec from CI or an editor without saving anything

GET /api/v1/kernel-runs?ids=a,b,c — batch-read up to 20 runs in one call

Machine-Readable Contracts

GET /api/v1/openapi.json — OpenAPI 3.1 document for the whole workflow-platform REST surface (unauthenticated, ETag-cached). Feed it to a codegen tool or hand it to your agent.

GET /api/v1/workflow-spec-schema — the WorkflowSpec JSON Schema (Draft 2020-12), for validating specs before you submit them.

ParameterTypeRequiredDescription
inputs object Required The workflow's declared inputs (typed; validated against the published spec).
version number Optional Pin a specific published version (default: latest published).
run_to_node string Optional Partial execution — run only up to this node.
binding_overrides object Optional Per-run capability overrides (e.g. route a step to your own BYOK credential where the creator allows it).
metadata object Optional Your opaque attribution data (≤16 string entries; keys ≤64 / values ≤256 chars). Echoed on every read and in webhook payloads.
webhook_url string Optional HTTPS URL for signed lifecycle webhooks (kernel_run.completed|failed|cancelled|awaiting_approval) — same signature scheme as all Wavemaker webhooks.
webhook_secret string Optional HMAC secret for the X-Webhook-Signature header (requires webhook_url).

Training Studio

Train your own model assets — image/video LoRAs and voice clones — from datasets you curate, then bind them into workflows. The full loop is REST (and MCP: create_dataset, add_dataset_items, pick_checkpoint, cancel_training_job). Product guide: Training Studio. Training requires a paid plan; failed jobs auto-refund.

POST /api/v1/training-jobs API Key

Start a training job on a ready dataset. Types: image_lora (60 cr, 8–400 images), video_lora (200 cr, 6–200 clips), wan_pair (300 cr), voice (30 cr, 1–25 samples). Accepts webhook_url / webhook_secret — you'll receive training_job.awaiting_checkpoint (with the selectable epochs) when sample grids are ready, and training_job.failed on failure.

Related Endpoints

POST /api/v1/datasets — create a dataset (image | video | voice; voice requires the consent record)

POST /api/v1/datasets/:id/uploads?filename=… — stream one media file (raw body, not multipart)

POST /api/v1/datasets/:id/items — register items: uploaded r2_keys, artifact ids from your runs, or HTTPS url items (fetched server-side with size caps and a fail-closed moderation scan; per-item rejections)

PATCH /api/v1/datasets/:id/items — bulk caption edits

GET /api/v1/training-jobs/:id — status + live progress (phase, percent) + checkpoints when done

POST /api/v1/training-jobs/:id/pick-checkpoint — finalize the winning epoch into a versioned model asset

POST /api/v1/training-jobs/:id/cancel — cancel (settles to actual; deliberately fires no webhook)

GET /api/v1/training-prices — the live type/price registry

Webhooks

Instead of polling, receive an HTTPS POST to your server when a video generation or render job completes or fails. Pass webhook_url (and optionally webhook_secret) when creating the job.

Delivery

POST with JSON body. Your endpoint must respond 2xx within 25 seconds. URL must be HTTPS; private IPs, localhost, and cloud-metadata addresses are rejected at job-creation time. Up to 3 delivery attempts with exponential backoff starting at 5 seconds.

Verification

When webhook_secret is set, each request carries an X-Webhook-Signature: v1,<hmac> header. The signed content is the concatenation <webhookId>.<timestamp>.<body> (HMAC-SHA256, hex-encoded). Use X-Webhook-ID for idempotent processing.

Events
>
EventTrigger
video.completedManaged generation or refinement finished (status: complete). Signed when webhook_secret was set. Crash-recovery emergency saves still fire this event.
video.failedManaged generation or refinement errored (status: errored).
video.cancelledJob cancelled; credits settled/refunded.
render.completedAn MCP-initiated MP4 render finished. status is done; output.download_url contains the final URL. Render webhooks are not signedrender_video does not accept a webhook_secret.
render.failedAn MCP-initiated render failed. status is failed; error contains the reason.
tool_run.completedA workspace tool run finished — static image ads (POST /api/v1/static-ads / generate_static_ad), upscale_video, and any POST /api/v1/workspaces/:id/tools/:name call with a webhook_url. Payload carries run_id, workspace_id, tool, and the tool result. Signed when webhook_secret was supplied.
tool_run.failedA workspace tool run failed. status is error; error contains the reason. Credits auto-refund on runner crashes.
kernel_run.completed / .failed / .cancelledA workflow-platform run reached a terminal state (set webhook_url on POST /workflows/:id/runs). Payload carries run_id, workflow_id, spec_version, settled credits, your metadata, and (on success) the run outputs. Signed when webhook_secret was supplied.
kernel_run.awaiting_approvalA run parked at an approval gate. Payload carries instance_key — POST it to /api/v1/kernel-runs/:id/approval with a decision to resume.
training_job.awaiting_checkpointA training job finished and is waiting for a checkpoint pick (payload carries the selectable epochs). Set webhook_url on POST /api/v1/training-jobs.
training_job.failedA training job failed; credits settle to actual. Cancellation is your own call and deliberately emits no webhook.

Generation and refinement both emit video.* when webhook_url is set (SSE for progress; webhooks for terminal). Payloads: docs/public-api.md § Webhooks.

Headers
HeaderDescription
X-Webhook-IDUnique delivery identifier — use for idempotent processing.
X-Webhook-TimestampUnix timestamp of delivery (seconds).
X-Webhook-SignatureHMAC-SHA256 signature, present only when webhook_secret was supplied. Format: v1,<hex-hmac>. Signed content: <X-Webhook-ID>.<X-Webhook-Timestamp>.<raw body>.
User-AgentWavemaker/1.0
Payload: video.completed
{
  "event": "video.completed",
  "job_id": "wf_abc123def456789",
  "status": "complete",
  "composition_id": "comp_xyz",
  "created_at": "2026-04-01T12:03:45Z",
  "completed_at": "2026-04-01T12:03:45Z"
}
Payload: video.failed
{
  "event": "video.failed",
  "job_id": "wf_abc123def456789",
  "status": "errored",
  "composition_id": null,
  "error": "Generation failed — no composition could be saved",
  "created_at": "2026-04-01T12:01:12Z",
  "completed_at": "2026-04-01T12:01:12Z"
}
Payload: render.completed
{
  "event": "render.completed",
  "job_id": "rnd_xyz",
  "status": "done",
  "composition_id": "comp_xyz",
  "output": { "download_url": "https://.../final.mp4" },
  "created_at": "2026-04-01T12:05:00Z",
  "completed_at": "2026-04-01T12:08:14Z"
}
Payload: tool_run.completed
{
  "event": "tool_run.completed",
  "run_id": "wtr_abc123",
  "workspace_id": "ws_xyz",
  "tool": "generate_static_ad",
  "status": "complete",
  "result": { ... },
  "completed_at": "2026-04-01T12:06:30Z"
}

Usage

GET /api/v1/usage API Key

Retrieve usage statistics and remaining monthly quota for the calling API key. Quotas are evaluated server-side on every call to POST /api/v1/videos.

Query Parameters
ParameterTypeDefaultDescription
days integer 30 Lookback period in days (1–90). Clamped to max(1, min(days, 90)).
Response

200

{
  "period_days": 30,
  "total_requests": 142,
  "total_cost_cents": 8500,
  "by_action": {
    "video_generation": { "count": 42, "cost_cents": 8500 }
  },
  "quota": {
    "quota_cents": null,
    "used_cents": 8500,
    "remaining_cents": null,
    "unlimited": true
  }
}

All fields use snake_case. Billing is credit-based and org-scoped (enforced with 402 on POST /api/v1/videos), so the per-key dollar quota is reported as unlimited: quota.unlimited is true and quota.quota_cents / quota.remaining_cents are null.

MCP Integration

Wavemaker implements the Model Context Protocol, so you can use it as a tool in Cursor, Claude Desktop, Windsurf, or any MCP-compatible client. The managed media MCP endpoint is at https://api.wavemaker.io/mcp and uses OAuth 2.1 with PKCE — no API key required in your config. The first connection opens a browser tab where you sign in with your Wavemaker account; subsequent requests use an access token issued by Wavemaker. Product overview: /mcp (marketing page — not the protocol host). Setup walkthrough: connect blog post. Slug-run REST: /workflow-api. Comfy platform MCP → $PLATFORM_API_URL (not api.wavemaker.io) — see /platform.

Endpoints

/oauth/authorize — user consent & sign-in
/oauth/token — token issue / refresh
/oauth/register — dynamic client registration
Access tokens live 1 hour; refresh tokens 30 days. Plain PKCE is rejected (S256 only).

Scopes

video:generate · video:read · video:render
Scopes are advertised in the OAuth discovery metadata so clients can request them, but the server does not currently use the granted-scope claim as an access gate — every authenticated MCP client sees the full tool list. Plan for scope enforcement in a future release. The same 30-rpm rate limit applies to every MCP call.

Available Tools
generate_video
Generate a complete video from a natural language prompt. Optional wait=true blocks with streamed progress notifications.
get_status
Check progress, per-scene status, current activity, and (optionally) extended-thinking reasoning for any job.
refine_video
Refine an existing composition with a natural-language instruction.
get_reviews
Read the per-scene quality review report already computed during generation (status/score/issues + overall + consistency). Read-only; no credits.
cancel_generation
Cancel an in-flight generation/refinement by job_id and refund the reserved credits.
scrape_and_analyze
Scrape a URL (or up to 5 pages with depth=site) and return brand profile, images, content summary, and palette. Charges 5 credits (refunded on failure).
plan_video
Plan a video's structure, scenes, and storyboard from content + parameters. Charges 5 credits (refunded on failure).
compose_video
Compose a video from a structured plan and save the composition.
get_composition
Fetch a saved composition by ID. Returns the full composition JSON.
list_compositions
List recent compositions for the connected user/org. Includes render status and download URLs.
render_video
Export the composition to MP4. Returns a render_id and optionally fires a render.completed webhook.
get_render_status
Check progress and download URL for a render job. Supports wait=true.
generate_and_render
All-in-one: generate, wait, render, wait, return download URL. Blocks 5–20 min.
get_cost_estimate
Estimate credit cost for a generation plan before committing.
get_usage
View rolling-30-day usage and remaining credit quota for the connected key.
get_account
Credit balances (total / subscription / purchased) plus the recent ledger for the connected account.
list_brand_kits
Org brand kits — ids usable as brand_kit_id on create_workspace / POST /api/v1/workspaces.
create_workspace
Create a persistent workspace for chaining catalog tools (optional brand_kit_id, model_overrides). Pair with describe_tool / execute_tool in lean/compact mode.
generate_static_ad
Generate one platform-ready static image ad (or a 2–6 card carousel) from a brief or a brand site URL: planned copy, baked visuals, QC, exact platform pixels. Or set purpose: "image" for a plain image (prompt renders as-is) and image_model to pin the renderer. 8 credits each.
creative_pack_generate
Coordinated multi-master pack from one brief ( multi_ar_pmax / video_ad_sequence / hook_variant / photo_set). Optional idempotency_key (auto-minted if omitted; REST still requires the header). Poll with creative_pack_status. Not registered in lean/builder.
generate_audio
Downloadable MP3 set — Music, Voice, Spot (:06/:15/:30/:60 packs), or Podcast. Poll with generate_audio_status. Kill switch audioOutputModeEnabled (503 when OFF). Distinct from video soundtrack tools.
edit_audio_set
Edit a delivered audio set: regenerate, change_style, change_voice, rewrite_script, remix_bed, more_variants. Pass audio_set_id or audio_variant_id.
list_audio_styles
Free catalog of aus_* styles (optional purpose filter). Sibling: list_audio_treatments.
list_ad_formats
Catalog of static-ad formats and platform packs — exact delivery pixels, ratios, and per-platform recommended packs for generate_static_ad.
list_ad_styles
Deprecated — use list_image_styles. Catalog of ad-capable visual styles only.
list_image_styles
Catalog of every Image Style — ad-copy layouts AND plain-image looks, each with its applicable_purposes. style_id values for generate_static_ad / edit_static_ad (change_style).

Beyond this curated list, the MCP server also registers the full workspace tool catalog dynamically — every externally exposed pipeline tool (scraping, subject development, image/clip generation, review, composition, refinement, upscale_video, and more) as its own MCP tool, executed inside a persistent workspace. Browse it with GET /api/v1/tools or the describe_tool MCP tool. Catalog Video adds its own dco_* family (prefer composites dco_bootstrap_catalog, dco_run_produce, dco_get_status_bundle, dco_review_variants, dco_manage_delivery) — same service layer as /api/v1/dco/*.

Registration modes: default full exposes every static tool (~119, incl. DCO / creative packs / audio / kernel) plus the dynamic workspace catalog (~100 more; live count in docs/agent-capabilities.md). compact keeps all static tools and promotes the lean research/media/planning subset; remaining catalog stays behind describe_tool + execute_tool. lean (≤56) is meta + managed escape hatches + that promoted subset only — no DCO, creative packs, or hand-curated audio wrappers (audio generate/edit still reachable via execute_tool + get_tool_run). Set via CONFIG KV workspace_mcp:mode, header X-Wavemaker-Mcp-Tools, or ?tools=. Local Cursor against wrangler should use /mcp-api (API-key) rather than OAuth /mcp.

Builder mode (?tools=builder): the workflow-platform surface only, for agents that build and run workflows — kernel workflow/run/catalog tools (search_hub, run_hub_workflow, estimate_workflow, get_run), Training Studio tools (create_dataset, add_dataset_items, pick_checkpoint, …), draft_workflow (one-shot compiler-in-the-loop drafting from a plain-language description — free), dynamic wf_<slug> tools (your org's published workflows + your favorites register as first-class tools, cap 40), and billing — zero managed-video or DCO tools (~35 total). See API & MCP for the map.

Cursor MCP Configuration

Add this to your .cursor/mcp.json (project-local) or ~/.cursor/mcp.json (global). Cursor opens the OAuth browser flow on first use; no headers or keys go in the config.

{
  "mcpServers": {
    "wavemaker": {
      "url": "https://api.wavemaker.io/mcp"
    }
  }
}
Claude Desktop Configuration

Add to your claude_desktop_config.json. mcp-remote handles the OAuth flow and persists tokens locally.

{
  "mcpServers": {
    "wavemaker": {
      "command": "npx",
      "args": ["-y", "mcp-remote", "https://api.wavemaker.io/mcp"]
    }
  }
}

Other clients (Windsurf, VS Code MCP, Codex) connect the same way — point them at https://api.wavemaker.io/mcp as a remote streamable-HTTP MCP server. Direct curl to the MCP endpoint requires a valid OAuth access token in the Authorization header, which you obtain via the standard authorization-code + PKCE flow.

Errors

The API uses standard HTTP status codes. Error responses always include a JSON body with an error field describing the issue. Some 4xx responses also include a machine-readable code and a structured details object.

CodeMeaningDescription
200 OK Request succeeded.
202 Accepted Job dispatched. Response includes Retry-After.
400 Bad Request Invalid JSON, missing/short prompt, unknown aspect_ratio, or invalid webhook_url.
401 Unauthorized Missing/invalid Authorization header or API key.
402 Payment Required Insufficient credits, CREDITS_OWED from a settlement upcharge, or NO_BILLING_ORG (key's account has no organization initialized). Response carries code + details.
404 Not Found Job, composition, or resource does not exist (or is not visible to this API key).
429 Too Many Requests Per-key rate limit exceeded (retryAfterSeconds + Retry-After header) or monthly quota exceeded (response includes a usage snapshot).
500 Server Error Unexpected internal failure. Safe to retry with backoff.
503 Service Unavailable Dispatch infrastructure (Inngest) temporarily unreachable. Response includes Retry-After: 60.
Rate limit (HTTP 429)
{
  "error": "Rate limit exceeded",
  "retryAfterSeconds": 27
}
Quota exceeded (HTTP 429)
{
  "error": "Monthly quota exceeded",
  "usage": {
    "quotaCents": 5000,
    "usedCents": 5012,
    "remainingCents": 0
  }
}
Credits owed (HTTP 402)
{
  "error": "Organization has 25 credits owed from a prior settlement upcharge. Clear the balance before starting a new generation.",
  "code": "CREDITS_OWED",
  "details": { "owed": 25, "since": "2026-03-30T10:11:12Z" }
}

Ready to integrate? Create your API key to get started.

Manage API Keys