WizCut API
Programmatically upload, process, and render multicam podcast edits with the WizCut API.
The WizCut API lets you automate multicam podcast editing. Upload your camera angles, let WizCut detect speakers and generate cuts, optionally review them in a hosted editor, and get a rendered video back via webhook.
Authentication
Create an API key at wizcut.com/settings. Include it in every request:
Authorization: Bearer wc_live_your_key_here
Keys can be revoked at any time from the settings page.
Create a job
POST /api/jobs
{
"sources": [
{ "label": "Camera 1", "kind": "video", "ext": "mp4" },
{ "label": "Camera 2", "kind": "video", "ext": "mp4" }
],
"tracks": [
{ "sourceId": "auto", "speakers": [] }
],
"callbackUrl": "https://your-server.com/webhook",
"review": true
}
Fields:
| Field | Type | Description |
|---|---|---|
sources | array | Camera angles or audio files. Each has label, optional kind ("video" or "audio", default "video"), optional ext (default "mp4"). |
tracks | array | Speaker-to-source mappings. Pass empty speakers [] to let WizCut auto-assign after analysis. |
callbackUrl | string | URL to receive webhook notifications on status changes. |
review | boolean | Default true. When true, the job pauses at “ready” for human review of cuts before rendering. When false, rendering starts automatically after speaker mapping is submitted. |
Response:
{
"jobId": "uuid",
"uploadUrls": {
"source-uuid-1": "https://presigned-upload-url...",
"source-uuid-2": "https://presigned-upload-url..."
}
}
Upload files
PUT your video files to each presigned URL from the response. No auth header needed — the URL is self-authenticating.
curl -X PUT -T camera1.mp4 "https://presigned-upload-url..."
curl -X PUT -T camera2.mp4 "https://presigned-upload-url..."
Start processing
POST /api/jobs/{jobId}/process
This kicks off the pipeline: audio sync, speaker detection, cut generation, and proxy rendering. The request returns immediately — processing happens asynchronously.
Response:
{ "jobId": "uuid", "status": "syncing" }
Get job status
GET /api/jobs/{jobId}
Returns the full job object including current status, sources, turns, and cuts.
Status values:
| Status | Meaning |
|---|---|
created | Job created, waiting for uploads |
syncing | Aligning audio across sources |
diarizing | Detecting speakers |
mapping | Needs speaker-to-source mapping |
ready | Cuts generated, ready for review or rendering |
rendering | Final render in progress |
complete | Render done, output_url available |
approved | Human approved the output |
failed | Something went wrong, see error_message |
Webhooks
When you provide a callbackUrl, WizCut sends POST requests on status transitions:
“mapping” webhook (speakers detected, needs human mapping):
{
"jobId": "uuid",
"status": "mapping",
"reviewUrl": "https://wizcut.com/jobs/uuid/edit?reviewToken=..."
}
Send a human to the reviewUrl to map speakers to camera sources and review cuts.
“ready” webhook (speaker mapping complete, cuts generated):
{
"jobId": "uuid",
"status": "ready",
"reviewUrl": "https://wizcut.com/jobs/uuid/edit?reviewToken=..."
}
“complete” webhook (render finished):
{
"jobId": "uuid",
"status": "complete",
"outputUrl": "https://presigned-download-url..."
}
“approved” webhook (human approved via review UI):
{
"jobId": "uuid",
"status": "approved",
"outputUrl": "https://presigned-download-url..."
}
“failed” webhook:
{
"jobId": "uuid",
"status": "failed",
"error": "description of what went wrong"
}
Speaker mapping and review (human-in-the-loop)
After speaker detection, the job enters “mapping” status. A human must map the detected speakers to camera sources — diarization identifies when someone speaks but not which camera they’re on.
The “mapping” webhook includes a reviewUrl — a signed link to the WizCut editor. Send a human there. In the editor, they:
- Map each detected speaker to a camera source
- Preview and edit the auto-generated cuts
- Click “Render” when satisfied (if
review: true, the default) - Optionally click “Approve” after reviewing the rendered output
With review: false, rendering starts automatically as soon as the speaker mapping is submitted — the human doesn’t get to review cuts before rendering.
Advanced: programmatic cut control
For fully automated pipelines that skip the UI entirely, you can manage cuts and rendering via API:
Update cuts:
PATCH /api/jobs/{jobId}/cuts
{
"cuts": [
{ "startMs": 0, "endMs": 5000, "sourceId": "source-uuid-1" },
{ "startMs": 5000, "endMs": 12000, "sourceId": "source-uuid-2" }
]
}
Trigger render:
POST /api/jobs/{jobId}/render
Approve output:
POST /api/jobs/{jobId}/approve
Error responses
All endpoints return errors in this format:
{ "error": "Description of what went wrong" }
Common HTTP status codes:
| Code | Meaning |
|---|---|
| 401 | Missing or invalid API key |
| 403 | Quota exceeded |
| 404 | Job not found or not owned by you |
| 409 | Invalid status transition (e.g. rendering a job that isn’t ready) |
| 500 | Server error |
| 503 | Service not available |
Complete flow
Here’s the full happy path for an AI agent or automation:
# 1. Create a job with two cameras
JOB=$(curl -s -X POST https://wizcut.com/api/jobs \
-H "Authorization: Bearer wc_live_your_key" \
-H "Content-Type: application/json" \
-d '{
"sources": [
{"label": "Camera 1", "kind": "video"},
{"label": "Camera 2", "kind": "video"}
],
"callbackUrl": "https://your-server.com/webhook"
}')
JOB_ID=$(echo $JOB | jq -r '.jobId')
# 2. Upload video files to the presigned URLs
curl -X PUT -T camera1.mp4 "$(echo $JOB | jq -r '.uploadUrls | to_entries[0].value')"
curl -X PUT -T camera2.mp4 "$(echo $JOB | jq -r '.uploadUrls | to_entries[1].value')"
# 3. Start processing
curl -s -X POST "https://wizcut.com/api/jobs/$JOB_ID/process" \
-H "Authorization: Bearer wc_live_your_key"
# 4. Wait for webhook: { status: "mapping", reviewUrl: "..." }
# Send a human to reviewUrl to map speakers and review cuts
# 5. Human maps speakers → edits cuts → clicks Render in the editor
# Wait for webhook: { status: "complete", outputUrl: "..." }
# 6. Download the rendered video from outputUrl
With auto-render after mapping (review: false):
# Same as above, but add "review": false to step 1
# Human still maps speakers at the reviewUrl (step 4)
# But rendering starts automatically after mapping — no cut review step
# You'll get: { status: "complete", outputUrl: "..." }