API Documentation

Generate videos programmatically via REST API, or connect Wavemaker as an MCP tool in Cursor, Claude Desktop, or any MCP-compatible client.

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

Quick Start

Generate a video in three steps: create a job, poll for status, then retrieve the result.

1. Create a video generation job
curl -X POST https://api.wavemakr.com/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.wavemakr.com/api/v1/videos/wf_abc123def456 \
  -H "Authorization: Bearer mcp_your_api_key"
3. Get the composition
curl https://api.wavemakr.com/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.wavemakr.com/assets/...",
    "assets/scene-1/image_400w.jpg": "https://api.wavemakr.com/assets/...",
    "assets/scene-1/image_800w.jpg": "https://api.wavemakr.com/assets/...",
    "assets/scene-2/video.mp4": "https://api.wavemakr.com/assets/...",
    "assets/scene-2/video_web.mp4": "https://api.wavemakr.com/assets/...",
    "assets/scene-2/video_preview.mp4": "https://api.wavemakr.com/assets/..."
  }
}

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.completedGeneration finished and a composition was saved. status is complete. If the crash-recovery path emergency-saved a composition, this event still fires (degraded but usable composition).
video.failedGeneration ended without a saved composition. status is errored and error carries a short reason.
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.

Refinement workflows (REST /api/v1/videos superseded by chat, or the MCP refine_video tool) do not emit webhooks today — only generation does. Listen via SSE (GET /api/v1/videos/:jobId/events) for refinement progress.

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"
}

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 MCP endpoint is at https://api.wavemakr.com/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.

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.
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.
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.wavemakr.com/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.wavemakr.com/mcp"]
    }
  }
}

Other clients (Windsurf, VS Code MCP, Codex) connect the same way — point them at https://api.wavemakr.com/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