Overview
The Video API turns your video files into streaming-ready, downloadable MP4s. Upload a file, optionally trim it, hide sensitive regions, or combine clips, then download the result. It accepts MP4, MOV, MKV, WebM and most other formats, up to 20 GiB and 8 hours per file.
| Base URL | https://api.videopiper.dev/v1 |
|---|---|
| Format | JSON requests and responses (Content-Type: application/json) |
| Auth | Authorization: Bearer <your API key> |
| Spec | openapi.yaml (OpenAPI 3.0). Import it into Postman, Insomnia or a code generator. |
Video bytes never pass through the API. You upload them directly to storage with short-lived pre-signed URLs, which keeps large uploads fast and reliable.
Authentication
Every request carries your API key as a bearer token:
Authorization: Bearer vpk_XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
- Your key is issued to you by the service owner. Treat it like a password: anyone who has it can upload videos as you.
- Each key belongs to one user. You only ever see your own videos; any other video ID answers
404 NOT_FOUND. - A missing, unknown or revoked key gets
401 UNAUTHORIZED. Revoked keys stop working within a minute.
Accounts and API keys
Sign up in the web app with your email (a 6-digit code is sent to you). Once signed in, create API keys on the Account page. Each key is shown once; you cannot retrieve it again. API keys can also be created programmatically with POST /account/keys when signed in with a session token. Your account shows your plan, usage this month and API keys.
Quickstart
Upload a video, wait for it to process, and download the MP4. Set two environment variables first:
export VIDEO_API="https://api.videopiper.dev/v1"
export VIDEO_API_KEY="vpk_..." # your key
# Uses jq to read JSON. Files up to 64 MiB are a single part (see Python/JavaScript for any size).
AUTH="Authorization: Bearer $VIDEO_API_KEY"; JSON="Content-Type: application/json"
# 1. Create the video
SIZE=$(wc -c < lesson.mp4 | tr -d ' ')
VIDEO_ID=$(curl -s -X POST "$VIDEO_API/videos" -H "$AUTH" -H "$JSON" \
-d "{\"fileName\":\"lesson.mp4\",\"sizeBytes\":$SIZE}" | jq -r .videoId)
# 2. Get an upload URL for part 1, and PUT the bytes to it (no Authorization header)
PART_URL=$(curl -s -X POST "$VIDEO_API/videos/$VIDEO_ID/parts" -H "$AUTH" -H "$JSON" \
-d '{"partNumbers":[1]}' | jq -r '.parts[0].url')
curl -s -T lesson.mp4 "$PART_URL"
# 3. Start processing
curl -s -X POST "$VIDEO_API/videos/$VIDEO_ID/complete" -H "$AUTH" -H "$JSON" -d '{}'
# 4. Wait for SUCCEEDED, then download
until [ "$(curl -s "$VIDEO_API/videos/$VIDEO_ID" -H "$AUTH" | jq -r .status)" != "RUNNING" ]; do sleep 10; done
curl -s "$VIDEO_API/videos/$VIDEO_ID" -H "$AUTH" | jq -r .status # expect SUCCEEDED
curl -s -o lesson-processed.mp4 "$(curl -s "$VIDEO_API/videos/$VIDEO_ID" -H "$AUTH" | jq -r .downloadUrl)"
# pip install requests. Handles any file size (parts are uploaded in turn).
import os, time, requests
API, KEY = os.environ["VIDEO_API"], os.environ["VIDEO_API_KEY"]
session = requests.Session()
session.headers["Authorization"] = f"Bearer {KEY}"
def call(method, path, **kwargs):
response = session.request(method, API + path, timeout=60, **kwargs)
if not response.ok:
error = response.json()
raise RuntimeError(f"{response.status_code} {error['error']}: {error['message']} (request {error['requestId']})")
return response.json()
path = "lesson.mp4"
size = os.path.getsize(path)
video = call("POST", "/videos", json={"fileName": os.path.basename(path), "sizeBytes": size, "maxHeight": 1080})
video_id, part_size, part_count = video["videoId"], video["partSize"], video["partCount"]
with open(path, "rb") as source:
for first in range(1, part_count + 1, 100): # up to 100 URLs per request
numbers = list(range(first, min(first + 100, part_count + 1)))
for part in call("POST", f"/videos/{video_id}/parts", json={"partNumbers": numbers})["parts"]:
source.seek((part["partNumber"] - 1) * part_size)
requests.put(part["url"], data=source.read(part["sizeBytes"]), timeout=600).raise_for_status()
call("POST", f"/videos/{video_id}/complete", json={})
while (status := call("GET", f"/videos/{video_id}"))["status"] in ("SUBMITTED", "RUNNING"):
time.sleep(10)
if status["status"] != "SUCCEEDED":
raise RuntimeError(f"Processing ended with {status['status']}")
with requests.get(status["downloadUrl"], stream=True, timeout=600) as download, open("lesson-processed.mp4", "wb") as out:
for chunk in download.iter_content(1 << 20):
out.write(chunk)
// Node.js 18+ (built-in fetch). Handles any file size (parts are uploaded in turn).
import { open, stat } from "node:fs/promises";
const API = process.env.VIDEO_API, KEY = process.env.VIDEO_API_KEY;
async function call(method, path, body) {
const response = await fetch(API + path, {
method,
headers: { Authorization: `Bearer ${KEY}`, "Content-Type": "application/json" },
body: body === undefined ? undefined : JSON.stringify(body),
});
const data = await response.json();
if (!response.ok) throw new Error(`${response.status} ${data.error}: ${data.message} (request ${data.requestId})`);
return data;
}
const path = "lesson.mp4";
const { size } = await stat(path);
const { videoId, partSize, partCount } = await call("POST", "/videos", { fileName: "lesson.mp4", sizeBytes: size });
const file = await open(path);
for (let first = 1; first <= partCount; first += 100) { // up to 100 URLs per request
const partNumbers = Array.from({ length: Math.min(100, partCount - first + 1) }, (_, i) => first + i);
const { parts } = await call("POST", `/videos/${videoId}/parts`, { partNumbers });
for (const part of parts) {
const bytes = Buffer.alloc(part.sizeBytes);
await file.read(bytes, 0, part.sizeBytes, (part.partNumber - 1) * partSize);
const put = await fetch(part.url, { method: "PUT", body: bytes }); // no Authorization header
if (!put.ok) throw new Error(`Part ${part.partNumber} failed: ${put.status}`);
}
}
await file.close();
await call("POST", `/videos/${videoId}/complete`, {});
let status;
do {
await new Promise((r) => setTimeout(r, 10_000));
status = await call("GET", `/videos/${videoId}`);
} while (["SUBMITTED", "RUNNING"].includes(status.status));
console.log(status.status, status.downloadUrl);
Short videos are usually ready within a minute or two of complete; long videos are processed in parallel chunks.
Concepts
Video lifecycle
| Status | Meaning |
|---|---|
UPLOADING | Created; waiting for parts and complete. |
READY_TO_START | Upload finished but processing didn't start; call complete again. |
RUNNING | Processing. progress shows the stages. |
SUCCEEDED | Done. downloadUrl is present. |
FAILED / TIMED_OUT | Processing failed. Check the file plays elsewhere, then upload again or reprocess. |
ABORTED | Cancelled. |
Priority processing
Paid plans get priority. Videos from paid plans are scheduled ahead of free-tier videos when capacity is contended, so paid customers never wait behind a free-tier backlog. Plan changes take effect within ~60 seconds.
Conventions
- IDs:
videoIdis 32 lowercase hex characters. - Times are seconds (decimals allowed). An
endofnullmeans "to the end". - Positions on screen (
x,y,w,h) are fractions of the frame from 0 to 1, so they work at every quality level. - Strict input: unknown fields are refused with
400 INVALID_REQUEST, so a typo can't be silently ignored. - Results: each finished video has an MP4 (H.264/AAC, with fast start for streaming playback).
Uploading videos
- Create the video with its file name and exact size:
POST /videos. You get avideoId, apartSize(64 MiB) and apartCount. - Get part URLs:
POST /videos/{videoId}/partswith up to 100partNumbersper call. Each URL is valid for 15 minutes, so request them shortly before use. - Upload each part with
PUT <url>. Part n is the byte range starting at(n − 1) × partSize, exactlysizeByteslong. Send noAuthorizationheader. Parts can upload in parallel (4–8 at a time works well) and in any order. - Complete:
POST /videos/{videoId}/complete, optionally with processing options. Processing starts.
POST /videos
{ "fileName": "lesson.mp4", "sizeBytes": 734003200, "maxHeight": 1080 }
201 Created
{ "videoId": "3f1c0a9e5b7d4c2a8e6f1b0d9c7a5e3f", "partSize": 67108864, "partCount": 11,
"status": "UPLOADING", "statusPath": "/videos/3f1c0a9e5b7d4c2a8e6f1b0d9c7a5e3f" }
POST /videos/3f1c…/parts
{ "partNumbers": [1, 2, 3] }
200 OK
{ "parts": [ { "partNumber": 1, "sizeBytes": 67108864, "url": "https://…" }, … ], "expiresInSeconds": 900 }
- Retries: if a part fails or its URL expires, request a new URL for that part number and upload it again.
completeis safe to call more than once: it never starts a second job. - Abandoned uploads (never completed) are deleted automatically after 2 days. To discard one sooner, call
POST /videos/{videoId}/abort. - Incomplete uploads:
completeanswers409 UPLOAD_INCOMPLETEif a part is missing or the wrong size.
Import from a URL
Instead of uploading a file, send a public HTTPS URL and the API downloads it. The file is checked once it arrives and processed immediately, just like an upload. Processing options (trim, redactions, deleteOriginal) go in the request.
The URL must be HTTPS and resolve only to public internet addresses. The file size limit is the same as uploads (20 GiB by default). The server must answer 200 with the file (at most 3 redirects, each re-checked); Dropbox share links need ?dl=1, and presigned S3 links work fine.
POST /videos
{ "sourceUrl": "https://example.com/videos/tutorial.mp4", "fileName": "tutorial.mp4", "maxHeight": 1080 }
201 Created
{ "videoId": "3f1c0a9e5b7d4c2a8e6f1b0d9c7a5e3f", "status": "SUBMITTED",
"fileName": "tutorial.mp4", "statusPath": "/videos/3f1c0a9e5b7d4c2a8e6f1b0d9c7a5e3f" }
Unlike an upload, there is no parts step or complete call. Dry runs (?dryRun=true) validate the URL without downloading. While the download is in progress the video record has no sourceVersionId; it fills in from the receipt once the file arrives. The download is retried once on network errors; 4xx and size-limit errors are permanent.
AUTH="Authorization: Bearer $VIDEO_API_KEY"; JSON="Content-Type: application/json"
# Create the video from a URL
VIDEO_ID=$(curl -s -X POST "$VIDEO_API/videos" -H "$AUTH" -H "$JSON" \
-d '{"sourceUrl":"https://example.com/videos/tutorial.mp4","fileName":"tutorial.mp4","maxHeight":1080}' | jq -r .videoId)
# Poll status until ready
until [ "$(curl -s "$VIDEO_API/videos/$VIDEO_ID" -H "$AUTH" | jq -r .status)" != "RUNNING" ]; do sleep 10; done
# Download
curl -s -o tutorial-processed.mp4 "$(curl -s "$VIDEO_API/videos/$VIDEO_ID" -H "$AUTH" | jq -r .downloadUrl)"
import os, time, requests
API, KEY = os.environ["VIDEO_API"], os.environ["VIDEO_API_KEY"]
session = requests.Session()
session.headers["Authorization"] = f"Bearer {KEY}"
video = session.request("POST", API + "/videos", json={
"sourceUrl": "https://example.com/videos/tutorial.mp4",
"fileName": "tutorial.mp4",
"maxHeight": 1080
}, timeout=60).json()
video_id = video["videoId"]
status = {"status": "SUBMITTED"}
while status["status"] in ("SUBMITTED", "RUNNING"):
time.sleep(10)
status = session.request("GET", API + f"/videos/{video_id}", timeout=60).json()
if status["status"] != "SUCCEEDED":
raise RuntimeError(f"Processing ended with {status['status']}")
with requests.get(status["downloadUrl"], stream=True) as download, open("tutorial-processed.mp4", "wb") as out:
for chunk in download.iter_content(1024*1024):
out.write(chunk)
Limits and errors
- URL format: must be HTTPS, 1–2,048 characters, with no username or password embedded.
- Redirects: at most 3; each destination is checked the same way (HTTPS, public internet).
- Content-Length: checked up front; if the declared size exceeds the limit, the import fails immediately.
- Network errors: retried once (e.g. timeout, connection reset). Other errors are permanent: 404, size limit hit, empty file, DNS failure, private address.
- No camera / streaming: imports work only with single files. Use regular uploads for screen + camera or upload-while-recording.
- Idempotency: `Idempotency-Key` works the same way as for uploads and edits.
Uploads from a browser
To let your users upload from your web app without exposing your key, split the work between your backend and the browser:
- The browser tells your backend the file name and size.
- Your backend calls
POST /videosandPOST /videos/{id}/parts, and returns only thevideoId, thepartSizeand the part URLs. - The browser
PUTs each slice (file.slice(start, start + sizeBytes)) straight to its URL. Storage accepts cross-originPUTs, and exposes theETagheader. - The browser tells your backend it's done; your backend calls
complete.
// Browser: upload the slices your backend signed
for (const part of parts) {
const start = (part.partNumber - 1) * partSize;
const response = await fetch(part.url, { method: "PUT", body: file.slice(start, start + part.sizeBytes) });
if (!response.ok) throw new Error(`Part ${part.partNumber} failed`);
}
For playback or download in the browser, pass the downloadUrl from a status response to the page. It's valid for 15 minutes.
Warm-up for faster processing
Request parts progressively as you upload (e.g., every few parts) rather than requesting all parts at once from the beginning. When you request the last few parts (within 192 MiB from the end of the file), the API automatically starts the processing container early so it boots while those final parts upload. This can cut total processing time nearly in half.
This happens automatically—no extra API calls needed. Asking for all parts at once, starting from part 1, skips warm-up (unless the whole file is ≤192 MiB). The web UI and example code already request parts progressively, so you get this benefit by default.
Screen + camera
Upload a screen recording and a camera recording together. The result shows the camera in the bottom-right corner of the screen, with both audio tracks mixed. Record both in the same session so they line up.
POST /videos
{ "fileName": "screen.webm", "sizeBytes": 209715200,
"layout": "pip", "camera": { "fileName": "camera.webm", "sizeBytes": 52428800 } }
The response adds camera.partSize and camera.partCount. Request the camera's part URLs with "file": "camera", upload both files, then call complete once.
Processing options
Send these in the body of complete (and of reprocess). maxHeight is set on create.
| Option | Type | Description |
|---|---|---|
maxHeight | 720 | 1080 | 1440 | 2160 | Highest quality produced; default 1080. Every lower level is made too. Never upscales; higher takes longer. |
trim | {start, end} | Keep only this part, in seconds into the upload. Frame-accurate; costs no extra processing time. |
redactions | array (≤ 20) | Regions to blur or black out, each during a time window. See below. |
deleteOriginal | boolean | Delete your uploaded original once the result is published. Use it with redactions when the unredacted file must not be kept. The video can't be reprocessed afterwards. |
POST /videos/3f1c…/complete
{
"trim": { "start": 4.5, "end": 312 },
"redactions": [
{ "type": "box", "x": 0.05, "y": 0.04, "w": 0.30, "h": 0.06, "start": 0, "end": null },
{ "type": "blur", "shape": "ellipse", "x": 0.62, "y": 0.55, "w": 0.20, "h": 0.30, "start": 12.5, "end": 20 }
]
}
Redaction fields
| Field | Description |
|---|---|
type | "box" (solid black bar: irreversible, the safest choice for text) or "blur". |
shape | "rect" (default) or "ellipse" (fills the box). |
x, y, w, h | Top-left corner and size, as fractions of the final frame (0–1). For screen + camera videos that's the combined picture. |
start, end | Seconds on the output timeline (after any trim). end: null = until the end. |
Following moving content
For something that moves, like a face walking across the frame, give the region a track of keyframes instead of x, y, w, h. It glides smoothly between them, resizing as it goes:
{ "type": "blur", "shape": "ellipse", "start": 4, "end": 12,
"track": [ { "time": 4, "x": 0.10, "y": 0.30, "w": 0.15, "h": 0.25 },
{ "time": 8, "x": 0.45, "y": 0.28, "w": 0.18, "h": 0.30 },
{ "time": 12, "x": 0.80, "y": 0.30, "w": 0.15, "h": 0.25 } ] }
Add a keyframe wherever the motion changes direction or speed. Up to 50 keyframes per region.
Trim, stitch & splice
An edit makes a new video from clips of your finished videos (status SUCCEEDED). Send POST /videos with an edit instead of a file. There's nothing to upload; poll the new video's status as usual.
| Clip field | Description |
|---|---|
videoId | One of your finished videos. The same video may appear several times. |
start, end | Seconds into that video (default: all of it). |
transition | How the clip starts from the previous one: omitted = hard cut; {"type": "fade" | "fadeblack", "seconds": 0.1–3}. |
Also accepted: fileName, maxHeight, and redactions (on the edit's own timeline). Up to 50 clips. The first clip sets the frame size; differently shaped clips are letterboxed.
Recipes
// Cut 1:00–1:30 out of a video
{ "edit": { "clips": [ { "videoId": "AAAA…", "end": 60 }, { "videoId": "AAAA…", "start": 90 } ] } }
// Stitch an intro, a lesson and an outro, with soft joins
{ "fileName": "Lesson 1", "edit": { "clips": [
{ "videoId": "INTRO…" },
{ "videoId": "LESSON…", "transition": { "type": "fade", "seconds": 0.75 } },
{ "videoId": "OUTRO…", "transition": { "type": "fadeblack", "seconds": 1 } } ] } }
// Splice a clip into another at 2:00
{ "edit": { "clips": [
{ "videoId": "MAIN…", "end": 120 },
{ "videoId": "INSERT…", "transition": { "type": "fadeblack", "seconds": 0.5 } },
{ "videoId": "MAIN…", "start": 120, "transition": { "type": "fadeblack", "seconds": 0.5 } } ] } }
201 Created
{ "videoId": "9a8b…", "status": "SUBMITTED", "clips": 3, "statusPath": "/videos/9a8b…" }
A clip from someone else's video gets 404 NOT_FOUND; a video still processing gets 409 EDIT_SOURCE_NOT_READY. To trim a new upload, use the trim option on complete instead: it's faster.
Status & download
GET /videos/3f1c…
{
"videoId": "3f1c…",
"status": "SUCCEEDED",
"startedAt": 1790903142.5,
"readyAt": 1790903205.0,
"progress": { "planned": true, "chunksTotal": 4, "chunksDone": 4, "audioDone": true,
"videoSeconds": 312.4, "topRendition": "1080p" },
"downloadUrl": "https://…/download.mp4?…",
"downloadExpiresInSeconds": 900
}
- Poll every 5–15 seconds while the status is
SUBMITTEDorRUNNING. Don't poll faster than once a second per video. downloadUrlis valid for 15 minutes. Fetch the status again for a fresh link, and don't store the link.posterUrlis a still frame (JPEG) for thumbnails, also valid for 15 minutes.progressshows chunks encoded out of the total, plus audio;videoSecondsis the result's length.- Processing time is
readyAt − startedAt(Unix seconds). edit/trim(when used) echo exactly how the video was cut, so you can check what was requested.
Streaming (Pro and Scale)
On Pro and Scale, finished videos also have adaptive streaming copies (HLS), and the status includes a link any standard player can play. The player picks the quality that suits each viewer's connection.
"streamUrl": "https://…/stream/3f1c…/1790925000/9b2e…/master.m3u8",
"streamExpiresAt": 1790925000
- The link is valid for 6 hours (
streamExpiresAt, Unix seconds). Fetch the status again for a fresh one when you load a player; don't store it. - It works in Safari and iOS natively, and anywhere else with hls.js, video.js, Shaka or ExoPlayer, including on your own website.
- Streaming copies count toward your plan's storage. On other plans, videos get the download only.
<!-- hls.js where the browser can't play HLS itself -->
<video id="player" controls playsinline></video>
<script src="https://cdn.jsdelivr.net/npm/hls.js@1"></script>
<script>
const video = document.getElementById("player"), url = STREAM_URL; // from GET /videos/{id}
if (video.canPlayType("application/vnd.apple.mpegurl")) video.src = url;
else { const hls = new Hls(); hls.loadSource(url); hls.attachMedia(video); }
</script>
Listing your videos
GET /videos?limit=25
{ "videos": [ { "videoId": "9a8b…", "fileName": "Lesson 1", "createdAt": 1790903100, "state": "SUBMITTED", "clips": 3 }, … ],
"nextToken": "eyJ2…" }
The newest videos come first. Pass nextToken back to get the next page. state only tells uploading from submitted; call GET /videos/{id} for the live status.
Uploading while recording (advanced)
For live recordings, upload while the recording is still being made, so the result is ready moments after it stops:
- Create with
"streaming": true(nosizeBytes). Add"prestart": trueto have processing ready and waiting. - Request each part's URL with its exact size:
{"parts": [{"partNumber": 4, "sizeBytes": 8388608}]}. Every part except the last must be at least 5 MiB. - With
prestart, sendPOST /videos/{id}/heartbeatevery ~10 seconds while recording. If heartbeats stop for 2 minutes, the waiting job gives up. - When recording ends, call
completewith the totals:{"source": {"sizeBytes": …, "partCount": …}}. To cancel instead, callabort.
Usage and billing
Each user's usage is metered by month for billing. Call GET /usage?month=YYYY-MM to fetch processing and storage totals:
GET /usage?month=2026-10
{
"month": "2026-10",
"complete": true,
"processing": {
"videos": 5,
"outputMinutes": 126.45,
"billableMinutes": 158.60
},
"storage": {
"gbMonths": 4.237,
"storedGb": 1.506,
"note": "download MP4s, posters and originals; streaming (HLS) copies are not billed"
},
"delivery": {
"gb": null,
"note": "not metered yet"
}
}
Billing rules
- Processing: billable minutes per video =
max(60 seconds, ceil(output seconds)) / 60 × quality multiplier. Quality multiplier: ≤1080p ×1, 1440p ×2, 4K ×4. Counted when the video finishes. Each upload, reprocess and edit that completes counts as one. - Storage: GB-months = bytes kept × days kept / (1e9 × days in month). Includes download MP4s, posters and original uploads. HLS streaming copies are metered but not billed. Videos deleted in the past still contribute to the month's total up to their deletion date.
- Pricing: free plans have no overage. Paid plans charge $0.09/minute and $0.15/GB-month for anything over the plan's included limits.
- Delivery: download bandwidth is not metered yet.
Deleting videos
Call DELETE /videos/{id} to remove a video permanently. It cancels any running processing, removes all outputs and work files, and stops billing the video's storage immediately. Usage records stay forever as billing history. The response includes how many bytes were freed.
DELETE /videos/3f1c…
200 OK
{ "videoId": "3f1c…", "deleted": true, "originalDeleted": true, "freedBytes": 1234567890 }
Irreversible: deleted videos can't be reprocessed. If you reprocessed from the same original, it's still kept for other videos that need it.
Account and API keys
Sign up by sending your email to POST /auth/start, which emails a 6-digit code (5 attempts, 10 minute expiry). Verify it with POST /auth/verify, accepting the terms on your first sign-in, to get a sessionToken. Sessions last 30 days and are stored just like API keys but marked as temporary; use the token as a bearer token the same way.
When signed in (session token) or with your API key, you can:
GET /accountfor your email, plan name, limits, and this month's usage (minutes, storage).GET /account/keysto list your API keys (never the keys themselves; they're shown once at creation).POST /account/keys {label}to create a new key. Must be signed in with a session token.DELETE /account/keys/{keyId}to revoke a key. Must be signed in with a session token.POST /auth/signoutto end the session (session tokens only).
Plan limits your maximum quality output, processing minutes and storage per month. Free-plan limits reset on the 1st of the month (UTC). Once your free-plan limit is hit, POST /videos returns 403 PLAN_LIMIT_REACHED. Paid plans continue and the overage is billed at your plan's rate. Usage details are available at GET /account.
Webhooks
Get notified when videos finish or fail, and when you're approaching your plan's limits. Set up a webhook endpoint (URL) in the web app on your Account → Webhooks page, or with the API:
GET /webhook— your current configuration.PUT /webhook {url, events?, enabled?}— save or update an endpoint.DELETE /webhook— remove the endpoint.POST /webhook/rotate— generate a new signing secret (keep the URL).POST /webhook/test— send a test event and report the result immediately.GET /webhook/deliveries— the last 30 days of delivery attempts.
Events
Each account has one endpoint that receives events for all types it's subscribed to. The possible events are:
| Event | Sent when | Data |
|---|---|---|
video.ready | Processing succeeded. | videoId, fileName, statusPath, metadata (if set), durationSeconds, downloadUrl (valid 1 hour), downloadExpiresInSeconds, posterUrl, and if streaming: streamUrl. |
video.failed | Processing failed or timed out. | videoId, fileName, statusPath, metadata, status: FAILED or TIMED_OUT. |
video.canceled | Processing was aborted. | videoId, fileName, statusPath, metadata. |
video.deleted | A video was deleted. | videoId, fileName, statusPath, metadata. |
usage.threshold | 80% or 100% of your plan's monthly limits is reached (once each per month). | measure (minutes or storage), percent (80 or 100), used, included (plan's allowance), month (YYYY-MM), plan (your plan name), overage (whether you'll be billed for excess). |
Webhook envelope
Every event arrives as JSON with metadata about the event:
{
"id": "evt_a1b2c3d4e5f6g7h8",
"type": "video.ready",
"createdAt": "2025-01-15T14:30:45Z",
"data": { event-specific data }
}
Signature verification (Standard Webhooks)
Each delivery includes headers webhook-id, webhook-timestamp and webhook-signature that implement the Standard Webhooks specification, so libraries for any language can verify them:
// npm install standardwebhooks
import { Webhook } from "standardwebhooks";
const wh = new Webhook(webhookSecret); // your secret from the account page
try {
const payload = wh.verify(body, {
"webhook-id": req.headers["webhook-id"],
"webhook-timestamp": req.headers["webhook-timestamp"],
"webhook-signature": req.headers["webhook-signature"],
});
// payload is the parsed event; proceed
} catch (err) {
res.status(401).send("Unauthorized");
}
# pip install svix
from svix.webhooks import Webhook
wh = Webhook(webhook_secret) # your secret from the account page
try:
payload = wh.verify(body, {
"webhook-id": headers["webhook-id"],
"webhook-timestamp": headers["webhook-timestamp"],
"webhook-signature": headers["webhook-signature"],
})
# payload is the parsed event; proceed
except Exception as e:
return {"error": "Unauthorized"}, 401
// Manual verification (no library needed)
import crypto from "crypto";
const id = req.headers["webhook-id"];
const timestamp = req.headers["webhook-timestamp"];
const signature = req.headers["webhook-signature"];
const body = /* raw request body as string */;
// Extract the base64 part of the secret (after "whsec_")
const secretBytes = Buffer.from(webhookSecret.slice(6), "base64");
// Sign: HMAC-SHA256 of "id.timestamp.body"
const signed = crypto
.createHmac("sha256", secretBytes)
.update(`${id}.${timestamp}.${body}`)
.digest("base64");
// The header is "v1,"
const expected = `v1,${signed}`;
if (signature !== expected) {
res.status(401).send("Unauthorized");
} else {
// Valid; parse and process the event
}
Reliability and delivery
- Respond 2xx within 10 seconds. Your endpoint must be HTTPS and reachable from the public internet. Redirects (3xx) are not followed.
- Retry policy: if you don't answer 2xx or timeout, delivery is retried after 1 minute, 5 minutes, 15 minutes, 1 hour, 3 hours, 6 hours and 12 hours (8 attempts over ~22 hours).
- At-least-once delivery: events may arrive more than once. Deduplicate using the
webhook-idheader (unique per event). Events may arrive out of order; use timestamps if order matters. - Delivery log: check
GET /webhook/deliveriesto see recent attempts, their status codes, response bodies and errors (kept for 30 days).
URL safety
Webhook URLs must be HTTPS and resolve only to public internet addresses. Private addresses (like 10.0.0.1, 192.168.1.x, 127.0.0.1, localhost or cloud-internal networks) are rejected when you save the URL or on every delivery attempt. This protects you from SSRF attacks. Redirects are never followed; the URL you set is the URL we connect to.
Validation & dry runs
Requests are checked before anything is created or started, against the real lengths of your videos where they're known. That includes trims past the end, clips that don't fit their video, transitions longer than their clips, redaction boxes outside the frame, and times after a video ends. Unknown fields (typos) are refused, never ignored.
Every problem is reported at once in details, each with its field path:
400
{
"message": "edit.clips[0].end: 80s is past the end of video 3f1c…, which is 60.0s (1:00); use null for \"to the end\" (and 1 more problem)",
"error": "INVALID_REQUEST",
"requestId": "…",
"details": [
{ "field": "edit.clips[0].end", "message": "80s is past the end of video 3f1c…, which is 60.0s (1:00); use null for \"to the end\"" },
{ "field": "redactions[0].w", "message": "must be a fraction of the frame from 0 to 1; got 1.2 (pixels? divide by the frame size)" }
]
}
Dry run
Add ?dryRun=true to POST /videos (an upload or an edit), POST /videos/{id}/complete or POST /videos/{id}/reprocess to check a request without creating or starting anything. It doesn't count toward your daily video limit. A valid request answers 200 with "valid": true; an edit also tells you its resulting length:
POST /videos?dryRun=true
{ "edit": { "clips": [ { "videoId": "3f1c…", "end": 30 },
{ "videoId": "9a8b…", "transition": { "type": "fade", "seconds": 1 } } ] } }
200 OK
{ "valid": true, "dryRun": true, "fileName": "Edit of 2 clips", "durationSeconds": 49.0, "duration": "49.0s (0:49)", "clips": [ … ] }
Use dry runs to validate user input in your app (for example, an edit timeline a user built) before submitting it.
Idempotency
Send an Idempotency-Key header with requests that create or start something (POST /videos, POST /videos/{id}/complete, POST /videos/{id}/reprocess
How it works
- Format: 1–255 printable ASCII characters (no spaces). Choose any unique value per request.
- Replay: same key, same request = the first response, with header
Idempotent-Replayed: true. Store thevideoIdimmediately on the first request (don't wait to see if it's a replay). - Reuse error: same key, different request (body, path or method) =
409 IDEMPOTENCY_KEY_REUSED. Use a new key. - In-progress error: same key while the first request is still processing =
409 IDEMPOTENCY_IN_PROGRESS. Retry in a moment. - Only 2xx stored: after an error the key is free, so a corrected retry works.
- Expires: stored for 24 hours.
- Dry runs skip keys:
?dryRun=truedoesn't use or check the key.
Example
First request
POST /videos HTTP/1.1
Idempotency-Key: my-lesson-upload-20250101-abc123
Content-Type: application/json
{ "fileName": "lesson.mp4", "sizeBytes": 1048576 }
200 OK
{ "videoId": "3f1c0a9e5b7d4c2a8e6f1b0d9c7a5e3f", "partSize": 67108864, … }
Retry with same key (e.g., connection dropped)
POST /videos HTTP/1.1
Idempotency-Key: my-lesson-upload-20250101-abc123
200 OK (same response as first time)
Idempotent-Replayed: true
{ "videoId": "3f1c0a9e5b7d4c2a8e6f1b0d9c7a5e3f", "partSize": 67108864, … }
Metadata
Store your own key-value pairs on a video: lesson IDs, course names, student IDs, project codes, anything. Metadata appears in status, list responses and webhooks, and you can filter listings by it.
Rules
- Up to 20 keys per video.
- Keys: 1–40 characters; letters, digits,
_,.,-. - Values: strings, up to 500 characters each.
- Total: 4 KB maximum for all metadata on one video.
Set on create
Add a metadata object when creating a video (upload or edit):
POST /videos
{
"fileName": "lesson-1.mp4",
"sizeBytes": 1048576,
"metadata": { "lessonId": "123", "courseId": "algebra-1" }
}
Update with PATCH
Change metadata on a finished or in-progress video without re-uploading. Keys set to null are removed; other keys are set:
PATCH /videos/3f1c…
{ "metadata": { "lessonId": "456", "archived": null } }
200 OK
{ "videoId": "3f1c…", "metadata": { "lessonId": "456", "courseId": "algebra-1" } }
Filter listings
Find videos by metadata: GET /videos?metadata.lessonId=123. Each query can include up to 5 filters. Filters are AND'd together. The OpenAPI spec uses metadata.<key> as a placeholder for dynamic filter names.
Inheritance
When you reprocess a video, the new one gets the original's metadata unless you provide new metadata in the reprocess request.
Endpoints
All paths are relative to the base URL. Every field and response is in the API reference below, and in the spec: openapi.yaml · openapi.json.
| Endpoint | Description | Success |
|---|---|---|
GET/videos | List your videos, newest first (limit 1–100, nextToken). | 200 |
POST/videos | Create a video: an upload (fileName, sizeBytes, maxHeight, camera, streaming, prestart) or an edit (edit). | 201 |
POST/videos/{id}/parts | Pre-signed upload URLs: partNumbers (or parts with sizes, for streaming), file: source | camera. | 200 |
POST/videos/{id}/complete | Finish the upload and start processing, with options. | 202 |
GET/videos/{id} | Status, progress, timing and download link. | 200 |
POST/videos/{id}/reprocess | Process again as a new video with new options (maxHeight, trim, redactions, deleteOriginal). Nothing is re-uploaded, and options aren't inherited. Returns the new videoId. | 201 |
POST/videos/{id}/cancel | Stop processing. The upload is kept, so you can reprocess later. | 200 |
DELETE/videos/{id} | Remove a video permanently (irreversible). Cancels processing, removes outputs and work files. Stops billing storage. Returns {videoId, deleted: true, originalDeleted, freedBytes}. | 200 |
PATCH/videos/{id} | Update metadata: {metadata: {key: value, …}}. Set keys or null to remove them. | 200 |
POST/videos/{id}/abort | Discard an unfinished upload and its parts. | 200 |
POST/videos/{id}/heartbeat | Keep a pre-started recording job waiting (while recording). | 200 |
GET/usage | Usage for a calendar month: processing time and storage, for billing. Query param month=YYYY-MM (defaults to current month). Returns aggregated minutes, storage, and current storage in GB. | 200 |
POST/auth/start | Public (no auth). Email a sign-in code: {email}. Returns {sent: true, expiresInSeconds: 600}. | 200 |
POST/auth/verify | Public (no auth). Verify a code: {email, code, acceptTerms}. Returns {sessionToken, expiresAt, account, created}. | 200 |
POST/auth/signout | End a session (session token only). | 200 |
GET/account | Your account: email, plan, limits and this month's usage. | 200 |
GET/account/keys | Your API keys (never the keys themselves; shown once at creation). | 200 |
POST/account/keys | Create an API key: {label}. Signed-in sessions only. Returns {key, keyId, label, note}. | 201 |
DELETE/account/keys/{keyId} | Revoke an API key. Signed-in sessions only. | 200 |
GET/webhook | Your webhook configuration: endpoint URL, events, enabled status, secret and timestamps. | 200 |
PUT/webhook | Set or update your webhook endpoint: {url, events?, enabled?}. Returns the full config. | 200 |
DELETE/webhook | Remove your webhook endpoint. | 200 |
POST/webhook/rotate | Generate a new signing secret (keep the URL). Returns the full config. | 200 |
POST/webhook/test | Send a test event now and report the result: {ok, status, ms, error, response, eventId}. | 200 |
GET/webhook/deliveries | Recent delivery attempts (last 30 days, newest first). Returns {deliveries} array of {eventId, type, attempt, ok, status, ms, error, at, final}. | 200 |
API reference
Every operation, grouped like the spec. Open one to see its parameters, request body and responses, each with an example and the full schema. Load openapi.json or openapi.yaml into Postman, Insomnia or a code generator.
Videos
Create, list, check and delete videos (uploads and edits).
GET/videosList my videos (newest first)
Filter by metadata: include up to 5 parameters like metadata.lessonId=123. Filters are AND'd together.
Parameters
| Name | In | Type | Description |
|---|---|---|---|
limit | query | integer | |
nextToken | query | string | From the previous page |
metadata.<key> | query | string | Filter by metadata value (up to 5 filters; dynamic parameter name) |
Responses
200A page of videos
{
"videos": [
{
"videoId": "string",
"fileName": "string",
"cameraFileName": "string",
"createdAt": 0,
"sizeBytes": 0,
"state": "UPLOADING",
"redactions": 0,
"originalDeleted": false,
"clips": 0,
"reprocessOf": "string",
"trim": {
"start": 0,
"end": 0
},
"metadata": {}
}
],
"nextToken": "string"
}Schema
videosarray of VideoSummaryvideoIdstringfileNamestringcameraFileNamestringcreatedAtintegerUnix time
sizeBytesintegerstatestringCall GET /videos/{id} for live status
one of"UPLOADING","READY_TO_START","SUBMITTED"redactionsintegeroriginalDeletedbooleanclipsintegerPresent for edits
reprocessOfstringtrimTrimKeep only part of the upload (frame-accurate, no extra encode). Redaction times are on the trimmed timeline.
startnumbermin0· default0endnumbernull = to the end
metadataMetadataCustomer key-value pairs (up to 20 keys, 4 KB total). Keys are 1-40 [A-Za-z0-9_.-]; values are strings up to 500 chars.
nextTokenstring
401UNAUTHORIZED: missing, unknown or revoked API key
{
"message": "Daily limit reached (50 videos per 24 hours); try again later",
"error": "DAILY_LIMIT",
"requestId": "string",
"details": [
{
"field": "edit.clips[0].end",
"message": "80s is past the end of video …, which is 60.0s (1:00); use null for \"to the end\""
}
]
}Schema
messagestringrequiredHuman-readable; wording may change
errorstringrequiredStable machine-readable code (new codes may be added; fall back to the HTTP status)
one of"INVALID_REQUEST","UNAUTHORIZED","MISSING_AUTHENTICATION","FORBIDDEN","REQUEST_REJECTED","NOT_FOUND","ROUTE_NOT_FOUND","METHOD_NOT_ALLOWED","UPLOAD_INCOMPLETE","ALREADY_COMPLETED","ORIGINAL_UNAVAILABLE","EDIT_SOURCE_NOT_READY","NOT_AN_UPLOAD","IDEMPOTENCY_KEY_REUSED","IDEMPOTENCY_IN_PROGRESS","INVALID_STATE","TOO_LARGE","RATE_LIMITED","QUOTA_EXCEEDED","DAILY_LIMIT","VIDEOS_PAUSED","PLAN_UPGRADE_REQUIRED","PLAN_LIMIT_REACHED","INVALID_CODE","TERMS_REQUIRED","SESSION_REQUIRED","ACCOUNT_DISABLED","EMAIL_NOT_CONFIGURED","EMAIL_FAILED","UPSTREAM_ERROR","UNAVAILABLE"requestIdstringrequiredQuote this when reporting a problem
detailsarray of objectValidation errors only (400 INVALID_REQUEST) - every problem found, with its field path
fieldstringmessagestring
400INVALID_REQUEST: invalid body or parameter (the message names the field)
{
"message": "Daily limit reached (50 videos per 24 hours); try again later",
"error": "DAILY_LIMIT",
"requestId": "string",
"details": [
{
"field": "edit.clips[0].end",
"message": "80s is past the end of video …, which is 60.0s (1:00); use null for \"to the end\""
}
]
}Schema
messagestringrequiredHuman-readable; wording may change
errorstringrequiredStable machine-readable code (new codes may be added; fall back to the HTTP status)
one of"INVALID_REQUEST","UNAUTHORIZED","MISSING_AUTHENTICATION","FORBIDDEN","REQUEST_REJECTED","NOT_FOUND","ROUTE_NOT_FOUND","METHOD_NOT_ALLOWED","UPLOAD_INCOMPLETE","ALREADY_COMPLETED","ORIGINAL_UNAVAILABLE","EDIT_SOURCE_NOT_READY","NOT_AN_UPLOAD","IDEMPOTENCY_KEY_REUSED","IDEMPOTENCY_IN_PROGRESS","INVALID_STATE","TOO_LARGE","RATE_LIMITED","QUOTA_EXCEEDED","DAILY_LIMIT","VIDEOS_PAUSED","PLAN_UPGRADE_REQUIRED","PLAN_LIMIT_REACHED","INVALID_CODE","TERMS_REQUIRED","SESSION_REQUIRED","ACCOUNT_DISABLED","EMAIL_NOT_CONFIGURED","EMAIL_FAILED","UPSTREAM_ERROR","UNAVAILABLE"requestIdstringrequiredQuote this when reporting a problem
detailsarray of objectValidation errors only (400 INVALID_REQUEST) - every problem found, with its field path
fieldstringmessagestring
404NOT_FOUND: no such video for this user (wrong ID or someone else's)
{
"message": "Daily limit reached (50 videos per 24 hours); try again later",
"error": "DAILY_LIMIT",
"requestId": "string",
"details": [
{
"field": "edit.clips[0].end",
"message": "80s is past the end of video …, which is 60.0s (1:00); use null for \"to the end\""
}
]
}Schema
messagestringrequiredHuman-readable; wording may change
errorstringrequiredStable machine-readable code (new codes may be added; fall back to the HTTP status)
one of"INVALID_REQUEST","UNAUTHORIZED","MISSING_AUTHENTICATION","FORBIDDEN","REQUEST_REJECTED","NOT_FOUND","ROUTE_NOT_FOUND","METHOD_NOT_ALLOWED","UPLOAD_INCOMPLETE","ALREADY_COMPLETED","ORIGINAL_UNAVAILABLE","EDIT_SOURCE_NOT_READY","NOT_AN_UPLOAD","IDEMPOTENCY_KEY_REUSED","IDEMPOTENCY_IN_PROGRESS","INVALID_STATE","TOO_LARGE","RATE_LIMITED","QUOTA_EXCEEDED","DAILY_LIMIT","VIDEOS_PAUSED","PLAN_UPGRADE_REQUIRED","PLAN_LIMIT_REACHED","INVALID_CODE","TERMS_REQUIRED","SESSION_REQUIRED","ACCOUNT_DISABLED","EMAIL_NOT_CONFIGURED","EMAIL_FAILED","UPSTREAM_ERROR","UNAVAILABLE"requestIdstringrequiredQuote this when reporting a problem
detailsarray of objectValidation errors only (400 INVALID_REQUEST) - every problem found, with its field path
fieldstringmessagestring
429RATE_LIMITED (retry with backoff), QUOTA_EXCEEDED, DAILY_LIMIT or VIDEOS_PAUSED
{
"message": "Daily limit reached (50 videos per 24 hours); try again later",
"error": "DAILY_LIMIT",
"requestId": "string",
"details": [
{
"field": "edit.clips[0].end",
"message": "80s is past the end of video …, which is 60.0s (1:00); use null for \"to the end\""
}
]
}Schema
messagestringrequiredHuman-readable; wording may change
errorstringrequiredStable machine-readable code (new codes may be added; fall back to the HTTP status)
one of"INVALID_REQUEST","UNAUTHORIZED","MISSING_AUTHENTICATION","FORBIDDEN","REQUEST_REJECTED","NOT_FOUND","ROUTE_NOT_FOUND","METHOD_NOT_ALLOWED","UPLOAD_INCOMPLETE","ALREADY_COMPLETED","ORIGINAL_UNAVAILABLE","EDIT_SOURCE_NOT_READY","NOT_AN_UPLOAD","IDEMPOTENCY_KEY_REUSED","IDEMPOTENCY_IN_PROGRESS","INVALID_STATE","TOO_LARGE","RATE_LIMITED","QUOTA_EXCEEDED","DAILY_LIMIT","VIDEOS_PAUSED","PLAN_UPGRADE_REQUIRED","PLAN_LIMIT_REACHED","INVALID_CODE","TERMS_REQUIRED","SESSION_REQUIRED","ACCOUNT_DISABLED","EMAIL_NOT_CONFIGURED","EMAIL_FAILED","UPSTREAM_ERROR","UNAVAILABLE"requestIdstringrequiredQuote this when reporting a problem
detailsarray of objectValidation errors only (400 INVALID_REQUEST) - every problem found, with its field path
fieldstringmessagestring
503UNAVAILABLE: temporary failure; retry with backoff
{
"message": "Daily limit reached (50 videos per 24 hours); try again later",
"error": "DAILY_LIMIT",
"requestId": "string",
"details": [
{
"field": "edit.clips[0].end",
"message": "80s is past the end of video …, which is 60.0s (1:00); use null for \"to the end\""
}
]
}Schema
messagestringrequiredHuman-readable; wording may change
errorstringrequiredStable machine-readable code (new codes may be added; fall back to the HTTP status)
one of"INVALID_REQUEST","UNAUTHORIZED","MISSING_AUTHENTICATION","FORBIDDEN","REQUEST_REJECTED","NOT_FOUND","ROUTE_NOT_FOUND","METHOD_NOT_ALLOWED","UPLOAD_INCOMPLETE","ALREADY_COMPLETED","ORIGINAL_UNAVAILABLE","EDIT_SOURCE_NOT_READY","NOT_AN_UPLOAD","IDEMPOTENCY_KEY_REUSED","IDEMPOTENCY_IN_PROGRESS","INVALID_STATE","TOO_LARGE","RATE_LIMITED","QUOTA_EXCEEDED","DAILY_LIMIT","VIDEOS_PAUSED","PLAN_UPGRADE_REQUIRED","PLAN_LIMIT_REACHED","INVALID_CODE","TERMS_REQUIRED","SESSION_REQUIRED","ACCOUNT_DISABLED","EMAIL_NOT_CONFIGURED","EMAIL_FAILED","UPSTREAM_ERROR","UNAVAILABLE"requestIdstringrequiredQuote this when reporting a problem
detailsarray of objectValidation errors only (400 INVALID_REQUEST) - every problem found, with its field path
fieldstringmessagestring
operationId listVideos
POST/videosCreate a video (an upload, or an edit of finished videos)
Send an upload request, or an edit (no upload) to cut, join and splice your finished videos.
Parameters
| Name | In | Type | Description |
|---|---|---|---|
dryRun | query | boolean | Validate only: nothing is created or started (answers 200 with valid: true) |
Idempotency-Key | header | string | Unique key for idempotent requests: POST /videos, POST /videos/{id}/complete, POST /videos/{id}/reprocess. Same key with same request = replay of first response. Stored 24 hours. |
Request body required
{
"fileName": "lesson1.mp4",
"sizeBytes": 104857600,
"maxHeight": 1080,
"camera": {
"fileName": "string",
"sizeBytes": 0
},
"layout": "pip",
"streaming": false,
"prestart": false,
"metadata": {}
}Schema
fileNamestringrequiredsizeBytesintegerRequired unless streaming
maxHeightintegerHighest output quality; never upscales
one of720,1080,1440,2160· default1080cameraobjectOptional second file shown picture-in-picture over the first (screen + camera)
fileNamestringsizeBytesinteger
layoutstringOptional with camera
one of"pip"streamingbooleanSize unknown yet (e.g. still recording); parts are signed per size
prestartbooleanWith streaming
metadataMetadataCustomer key-value pairs (up to 20 keys, 4 KB total). Keys are 1-40 [A-Za-z0-9_.-]; values are strings up to 500 chars.
sourceUrlstring (uri)requiredHTTPS URL (public internet addresses only; no username/password) with the video file. At most 3 redirects, each checked. The server must answer 200; file size is checked against the upload limit.
max length2048fileNamestringDisplay name (default: last URL path segment). 1–200 characters.
maxHeightintegerHighest output quality; never upscales
one of720,1080,1440,2160· default1080trimTrimKeep only part of the upload (frame-accurate, no extra encode). Redaction times are on the trimmed timeline.
startnumbermin0· default0endnumbernull = to the end
redactionsarray of RedactionRegions to blur or black out
max items20typestringrequiredbox = solid black bar
one of"blur","box"shapestringone of"rect","ellipse"· default"rect"xnumberFraction of the final frame width
min0· max1ynumbermin0· max1wnumbermin0· max1hnumbermin0· max1startnumberSeconds
default0endnumberSeconds; null = until the end
trackarray of objectKeyframes for moving content, in time order; the region glides between them (instead of x, y, w, h)
max items50timenumberrequiredSeconds on the output timeline
min0xnumberrequiredmin0· max1ynumberrequiredmin0· max1wnumberrequiredmin0· max1hnumberrequiredmin0· max1
deleteOriginalbooleanDelete the imported file once the output is published
metadataMetadataCustomer key-value pairs (up to 20 keys, 4 KB total). Keys are 1-40 [A-Za-z0-9_.-]; values are strings up to 500 chars.
editobjectrequired- max items
50videoIdstringrequiredOne of your finished (SUCCEEDED) videos; may repeat
pattern"^[a-f0-9]{32}$"startnumberSeconds into that video
min0· default0endnumberSeconds into that video; null = to its end
transitionobjectHow this clip starts from the previous one (hard cut if omitted; not on the first clip)
typestringone of"fade","fadeblack"· default"fade"secondsnumbermin0.1· max3· default0.5
fileNamestringDisplay name (default: Edit of N clips)
maxHeightintegerone of720,1080,1440,2160· default1080redactionsarray of RedactionOn the edit's own timeline
max items20typestringrequiredbox = solid black bar
one of"blur","box"shapestringone of"rect","ellipse"· default"rect"xnumberFraction of the final frame width
min0· max1ynumbermin0· max1wnumbermin0· max1hnumbermin0· max1startnumberSeconds
default0endnumberSeconds; null = until the end
trackarray of objectKeyframes for moving content, in time order; the region glides between them (instead of x, y, w, h)
max items50timenumberrequiredSeconds on the output timeline
min0xnumberrequiredmin0· max1ynumberrequiredmin0· max1wnumberrequiredmin0· max1hnumberrequiredmin0· max1
metadataMetadataCustomer key-value pairs (up to 20 keys, 4 KB total). Keys are 1-40 [A-Za-z0-9_.-]; values are strings up to 500 chars.
Responses
201Created
{
"videoId": "string",
"partSize": 0,
"partCount": 0,
"camera": {
"partSize": 0,
"partCount": 0
},
"clips": 0,
"status": "UPLOADING"
}Schema
videoIdstringpartSizeintegerUploads only. Bytes per part (last part may be smaller)
partCountintegerUploads only
cameraobjectpartSizeintegerpartCountinteger
clipsintegerEdits only
statusstring
400INVALID_REQUEST: invalid body or parameter (the message names the field)
{
"message": "Daily limit reached (50 videos per 24 hours); try again later",
"error": "DAILY_LIMIT",
"requestId": "string",
"details": [
{
"field": "edit.clips[0].end",
"message": "80s is past the end of video …, which is 60.0s (1:00); use null for \"to the end\""
}
]
}Schema
messagestringrequiredHuman-readable; wording may change
errorstringrequiredStable machine-readable code (new codes may be added; fall back to the HTTP status)
one of"INVALID_REQUEST","UNAUTHORIZED","MISSING_AUTHENTICATION","FORBIDDEN","REQUEST_REJECTED","NOT_FOUND","ROUTE_NOT_FOUND","METHOD_NOT_ALLOWED","UPLOAD_INCOMPLETE","ALREADY_COMPLETED","ORIGINAL_UNAVAILABLE","EDIT_SOURCE_NOT_READY","NOT_AN_UPLOAD","IDEMPOTENCY_KEY_REUSED","IDEMPOTENCY_IN_PROGRESS","INVALID_STATE","TOO_LARGE","RATE_LIMITED","QUOTA_EXCEEDED","DAILY_LIMIT","VIDEOS_PAUSED","PLAN_UPGRADE_REQUIRED","PLAN_LIMIT_REACHED","INVALID_CODE","TERMS_REQUIRED","SESSION_REQUIRED","ACCOUNT_DISABLED","EMAIL_NOT_CONFIGURED","EMAIL_FAILED","UPSTREAM_ERROR","UNAVAILABLE"requestIdstringrequiredQuote this when reporting a problem
detailsarray of objectValidation errors only (400 INVALID_REQUEST) - every problem found, with its field path
fieldstringmessagestring
401UNAUTHORIZED: missing, unknown or revoked API key
{
"message": "Daily limit reached (50 videos per 24 hours); try again later",
"error": "DAILY_LIMIT",
"requestId": "string",
"details": [
{
"field": "edit.clips[0].end",
"message": "80s is past the end of video …, which is 60.0s (1:00); use null for \"to the end\""
}
]
}Schema
messagestringrequiredHuman-readable; wording may change
errorstringrequiredStable machine-readable code (new codes may be added; fall back to the HTTP status)
one of"INVALID_REQUEST","UNAUTHORIZED","MISSING_AUTHENTICATION","FORBIDDEN","REQUEST_REJECTED","NOT_FOUND","ROUTE_NOT_FOUND","METHOD_NOT_ALLOWED","UPLOAD_INCOMPLETE","ALREADY_COMPLETED","ORIGINAL_UNAVAILABLE","EDIT_SOURCE_NOT_READY","NOT_AN_UPLOAD","IDEMPOTENCY_KEY_REUSED","IDEMPOTENCY_IN_PROGRESS","INVALID_STATE","TOO_LARGE","RATE_LIMITED","QUOTA_EXCEEDED","DAILY_LIMIT","VIDEOS_PAUSED","PLAN_UPGRADE_REQUIRED","PLAN_LIMIT_REACHED","INVALID_CODE","TERMS_REQUIRED","SESSION_REQUIRED","ACCOUNT_DISABLED","EMAIL_NOT_CONFIGURED","EMAIL_FAILED","UPSTREAM_ERROR","UNAVAILABLE"requestIdstringrequiredQuote this when reporting a problem
detailsarray of objectValidation errors only (400 INVALID_REQUEST) - every problem found, with its field path
fieldstringmessagestring
403Plan does not allow this feature (e.g. streaming, 4K, screen recording) or plan limit reached (free tier only)
{
"message": "Daily limit reached (50 videos per 24 hours); try again later",
"error": "DAILY_LIMIT",
"requestId": "string",
"details": [
{
"field": "edit.clips[0].end",
"message": "80s is past the end of video …, which is 60.0s (1:00); use null for \"to the end\""
}
]
}Schema
messagestringrequiredHuman-readable; wording may change
errorstringrequiredStable machine-readable code (new codes may be added; fall back to the HTTP status)
one of"INVALID_REQUEST","UNAUTHORIZED","MISSING_AUTHENTICATION","FORBIDDEN","REQUEST_REJECTED","NOT_FOUND","ROUTE_NOT_FOUND","METHOD_NOT_ALLOWED","UPLOAD_INCOMPLETE","ALREADY_COMPLETED","ORIGINAL_UNAVAILABLE","EDIT_SOURCE_NOT_READY","NOT_AN_UPLOAD","IDEMPOTENCY_KEY_REUSED","IDEMPOTENCY_IN_PROGRESS","INVALID_STATE","TOO_LARGE","RATE_LIMITED","QUOTA_EXCEEDED","DAILY_LIMIT","VIDEOS_PAUSED","PLAN_UPGRADE_REQUIRED","PLAN_LIMIT_REACHED","INVALID_CODE","TERMS_REQUIRED","SESSION_REQUIRED","ACCOUNT_DISABLED","EMAIL_NOT_CONFIGURED","EMAIL_FAILED","UPSTREAM_ERROR","UNAVAILABLE"requestIdstringrequiredQuote this when reporting a problem
detailsarray of objectValidation errors only (400 INVALID_REQUEST) - every problem found, with its field path
fieldstringmessagestring
404Edit: a clip's videoId is not yours
{
"message": "Daily limit reached (50 videos per 24 hours); try again later",
"error": "DAILY_LIMIT",
"requestId": "string",
"details": [
{
"field": "edit.clips[0].end",
"message": "80s is past the end of video …, which is 60.0s (1:00); use null for \"to the end\""
}
]
}Schema
messagestringrequiredHuman-readable; wording may change
errorstringrequiredStable machine-readable code (new codes may be added; fall back to the HTTP status)
one of"INVALID_REQUEST","UNAUTHORIZED","MISSING_AUTHENTICATION","FORBIDDEN","REQUEST_REJECTED","NOT_FOUND","ROUTE_NOT_FOUND","METHOD_NOT_ALLOWED","UPLOAD_INCOMPLETE","ALREADY_COMPLETED","ORIGINAL_UNAVAILABLE","EDIT_SOURCE_NOT_READY","NOT_AN_UPLOAD","IDEMPOTENCY_KEY_REUSED","IDEMPOTENCY_IN_PROGRESS","INVALID_STATE","TOO_LARGE","RATE_LIMITED","QUOTA_EXCEEDED","DAILY_LIMIT","VIDEOS_PAUSED","PLAN_UPGRADE_REQUIRED","PLAN_LIMIT_REACHED","INVALID_CODE","TERMS_REQUIRED","SESSION_REQUIRED","ACCOUNT_DISABLED","EMAIL_NOT_CONFIGURED","EMAIL_FAILED","UPSTREAM_ERROR","UNAVAILABLE"requestIdstringrequiredQuote this when reporting a problem
detailsarray of objectValidation errors only (400 INVALID_REQUEST) - every problem found, with its field path
fieldstringmessagestring
409Edit: a clip's video has not finished processing
{
"message": "Daily limit reached (50 videos per 24 hours); try again later",
"error": "DAILY_LIMIT",
"requestId": "string",
"details": [
{
"field": "edit.clips[0].end",
"message": "80s is past the end of video …, which is 60.0s (1:00); use null for \"to the end\""
}
]
}Schema
messagestringrequiredHuman-readable; wording may change
errorstringrequiredStable machine-readable code (new codes may be added; fall back to the HTTP status)
one of"INVALID_REQUEST","UNAUTHORIZED","MISSING_AUTHENTICATION","FORBIDDEN","REQUEST_REJECTED","NOT_FOUND","ROUTE_NOT_FOUND","METHOD_NOT_ALLOWED","UPLOAD_INCOMPLETE","ALREADY_COMPLETED","ORIGINAL_UNAVAILABLE","EDIT_SOURCE_NOT_READY","NOT_AN_UPLOAD","IDEMPOTENCY_KEY_REUSED","IDEMPOTENCY_IN_PROGRESS","INVALID_STATE","TOO_LARGE","RATE_LIMITED","QUOTA_EXCEEDED","DAILY_LIMIT","VIDEOS_PAUSED","PLAN_UPGRADE_REQUIRED","PLAN_LIMIT_REACHED","INVALID_CODE","TERMS_REQUIRED","SESSION_REQUIRED","ACCOUNT_DISABLED","EMAIL_NOT_CONFIGURED","EMAIL_FAILED","UPSTREAM_ERROR","UNAVAILABLE"requestIdstringrequiredQuote this when reporting a problem
detailsarray of objectValidation errors only (400 INVALID_REQUEST) - every problem found, with its field path
fieldstringmessagestring
429Rate limit or daily video limit
{
"message": "Daily limit reached (50 videos per 24 hours); try again later",
"error": "DAILY_LIMIT",
"requestId": "string",
"details": [
{
"field": "edit.clips[0].end",
"message": "80s is past the end of video …, which is 60.0s (1:00); use null for \"to the end\""
}
]
}Schema
messagestringrequiredHuman-readable; wording may change
errorstringrequiredStable machine-readable code (new codes may be added; fall back to the HTTP status)
one of"INVALID_REQUEST","UNAUTHORIZED","MISSING_AUTHENTICATION","FORBIDDEN","REQUEST_REJECTED","NOT_FOUND","ROUTE_NOT_FOUND","METHOD_NOT_ALLOWED","UPLOAD_INCOMPLETE","ALREADY_COMPLETED","ORIGINAL_UNAVAILABLE","EDIT_SOURCE_NOT_READY","NOT_AN_UPLOAD","IDEMPOTENCY_KEY_REUSED","IDEMPOTENCY_IN_PROGRESS","INVALID_STATE","TOO_LARGE","RATE_LIMITED","QUOTA_EXCEEDED","DAILY_LIMIT","VIDEOS_PAUSED","PLAN_UPGRADE_REQUIRED","PLAN_LIMIT_REACHED","INVALID_CODE","TERMS_REQUIRED","SESSION_REQUIRED","ACCOUNT_DISABLED","EMAIL_NOT_CONFIGURED","EMAIL_FAILED","UPSTREAM_ERROR","UNAVAILABLE"requestIdstringrequiredQuote this when reporting a problem
detailsarray of objectValidation errors only (400 INVALID_REQUEST) - every problem found, with its field path
fieldstringmessagestring
503UNAVAILABLE: temporary failure; retry with backoff
{
"message": "Daily limit reached (50 videos per 24 hours); try again later",
"error": "DAILY_LIMIT",
"requestId": "string",
"details": [
{
"field": "edit.clips[0].end",
"message": "80s is past the end of video …, which is 60.0s (1:00); use null for \"to the end\""
}
]
}Schema
messagestringrequiredHuman-readable; wording may change
errorstringrequiredStable machine-readable code (new codes may be added; fall back to the HTTP status)
one of"INVALID_REQUEST","UNAUTHORIZED","MISSING_AUTHENTICATION","FORBIDDEN","REQUEST_REJECTED","NOT_FOUND","ROUTE_NOT_FOUND","METHOD_NOT_ALLOWED","UPLOAD_INCOMPLETE","ALREADY_COMPLETED","ORIGINAL_UNAVAILABLE","EDIT_SOURCE_NOT_READY","NOT_AN_UPLOAD","IDEMPOTENCY_KEY_REUSED","IDEMPOTENCY_IN_PROGRESS","INVALID_STATE","TOO_LARGE","RATE_LIMITED","QUOTA_EXCEEDED","DAILY_LIMIT","VIDEOS_PAUSED","PLAN_UPGRADE_REQUIRED","PLAN_LIMIT_REACHED","INVALID_CODE","TERMS_REQUIRED","SESSION_REQUIRED","ACCOUNT_DISABLED","EMAIL_NOT_CONFIGURED","EMAIL_FAILED","UPSTREAM_ERROR","UNAVAILABLE"requestIdstringrequiredQuote this when reporting a problem
detailsarray of objectValidation errors only (400 INVALID_REQUEST) - every problem found, with its field path
fieldstringmessagestring
operationId createVideo
GET/videos/{videoId}Status, timing and (when finished) a 15-minute download link
Parameters
| Name | In | Type | Description |
|---|---|---|---|
videoIdrequired | path | string |
Responses
200Current state
{
"videoId": "string",
"status": "UPLOADING",
"readyAt": 0,
"downloadUrl": "string",
"posterUrl": "string",
"streamUrl": "string",
"streamExpiresAt": 0,
"startedAt": 0,
"stoppedAt": 0,
"progress": {
"planned": false,
"chunksTotal": 0,
"chunksDone": 0,
"audioDone": false,
"videoSeconds": 0,
"topRendition": "1080p"
},
"downloadExpiresInSeconds": 0,
"result": {
"jobId": "string",
"downloadAvailable": false,
"streamAvailable": false
}
}Schema
videoIdstringstatusstringone of"UPLOADING","READY_TO_START","RUNNING","SUCCEEDED","FAILED","TIMED_OUT","ABORTED","PENDING_REDRIVE","HISTORY_UNAVAILABLE"readyAtnumberUnix time the output was published
downloadUrlstringPre-signed MP4 link (SUCCEEDED only)
posterUrlstringPre-signed JPEG still frame for thumbnails (SUCCEEDED only)
streamUrlstringHLS master playlist link (Pro and Scale plans only; valid 6 hours)
streamExpiresAtnumberUnix time the streaming link expires
startedAtnumberUnix time processing started
stoppedAtnumberprogressobjectplannedbooleanchunksTotalintegerchunksDoneintegeraudioDonebooleanvideoSecondsnumbertopRenditionstring
downloadExpiresInSecondsintegerresultobjectSUCCEEDED only - publication status
jobIdstringdownloadAvailablebooleanstreamAvailablebooleanWhether a playable streaming link exists
404NOT_FOUND: no such video for this user (wrong ID or someone else's)
{
"message": "Daily limit reached (50 videos per 24 hours); try again later",
"error": "DAILY_LIMIT",
"requestId": "string",
"details": [
{
"field": "edit.clips[0].end",
"message": "80s is past the end of video …, which is 60.0s (1:00); use null for \"to the end\""
}
]
}Schema
messagestringrequiredHuman-readable; wording may change
errorstringrequiredStable machine-readable code (new codes may be added; fall back to the HTTP status)
one of"INVALID_REQUEST","UNAUTHORIZED","MISSING_AUTHENTICATION","FORBIDDEN","REQUEST_REJECTED","NOT_FOUND","ROUTE_NOT_FOUND","METHOD_NOT_ALLOWED","UPLOAD_INCOMPLETE","ALREADY_COMPLETED","ORIGINAL_UNAVAILABLE","EDIT_SOURCE_NOT_READY","NOT_AN_UPLOAD","IDEMPOTENCY_KEY_REUSED","IDEMPOTENCY_IN_PROGRESS","INVALID_STATE","TOO_LARGE","RATE_LIMITED","QUOTA_EXCEEDED","DAILY_LIMIT","VIDEOS_PAUSED","PLAN_UPGRADE_REQUIRED","PLAN_LIMIT_REACHED","INVALID_CODE","TERMS_REQUIRED","SESSION_REQUIRED","ACCOUNT_DISABLED","EMAIL_NOT_CONFIGURED","EMAIL_FAILED","UPSTREAM_ERROR","UNAVAILABLE"requestIdstringrequiredQuote this when reporting a problem
detailsarray of objectValidation errors only (400 INVALID_REQUEST) - every problem found, with its field path
fieldstringmessagestring
400INVALID_REQUEST: invalid body or parameter (the message names the field)
{
"message": "Daily limit reached (50 videos per 24 hours); try again later",
"error": "DAILY_LIMIT",
"requestId": "string",
"details": [
{
"field": "edit.clips[0].end",
"message": "80s is past the end of video …, which is 60.0s (1:00); use null for \"to the end\""
}
]
}Schema
messagestringrequiredHuman-readable; wording may change
errorstringrequiredStable machine-readable code (new codes may be added; fall back to the HTTP status)
one of"INVALID_REQUEST","UNAUTHORIZED","MISSING_AUTHENTICATION","FORBIDDEN","REQUEST_REJECTED","NOT_FOUND","ROUTE_NOT_FOUND","METHOD_NOT_ALLOWED","UPLOAD_INCOMPLETE","ALREADY_COMPLETED","ORIGINAL_UNAVAILABLE","EDIT_SOURCE_NOT_READY","NOT_AN_UPLOAD","IDEMPOTENCY_KEY_REUSED","IDEMPOTENCY_IN_PROGRESS","INVALID_STATE","TOO_LARGE","RATE_LIMITED","QUOTA_EXCEEDED","DAILY_LIMIT","VIDEOS_PAUSED","PLAN_UPGRADE_REQUIRED","PLAN_LIMIT_REACHED","INVALID_CODE","TERMS_REQUIRED","SESSION_REQUIRED","ACCOUNT_DISABLED","EMAIL_NOT_CONFIGURED","EMAIL_FAILED","UPSTREAM_ERROR","UNAVAILABLE"requestIdstringrequiredQuote this when reporting a problem
detailsarray of objectValidation errors only (400 INVALID_REQUEST) - every problem found, with its field path
fieldstringmessagestring
401UNAUTHORIZED: missing, unknown or revoked API key
{
"message": "Daily limit reached (50 videos per 24 hours); try again later",
"error": "DAILY_LIMIT",
"requestId": "string",
"details": [
{
"field": "edit.clips[0].end",
"message": "80s is past the end of video …, which is 60.0s (1:00); use null for \"to the end\""
}
]
}Schema
messagestringrequiredHuman-readable; wording may change
errorstringrequiredStable machine-readable code (new codes may be added; fall back to the HTTP status)
one of"INVALID_REQUEST","UNAUTHORIZED","MISSING_AUTHENTICATION","FORBIDDEN","REQUEST_REJECTED","NOT_FOUND","ROUTE_NOT_FOUND","METHOD_NOT_ALLOWED","UPLOAD_INCOMPLETE","ALREADY_COMPLETED","ORIGINAL_UNAVAILABLE","EDIT_SOURCE_NOT_READY","NOT_AN_UPLOAD","IDEMPOTENCY_KEY_REUSED","IDEMPOTENCY_IN_PROGRESS","INVALID_STATE","TOO_LARGE","RATE_LIMITED","QUOTA_EXCEEDED","DAILY_LIMIT","VIDEOS_PAUSED","PLAN_UPGRADE_REQUIRED","PLAN_LIMIT_REACHED","INVALID_CODE","TERMS_REQUIRED","SESSION_REQUIRED","ACCOUNT_DISABLED","EMAIL_NOT_CONFIGURED","EMAIL_FAILED","UPSTREAM_ERROR","UNAVAILABLE"requestIdstringrequiredQuote this when reporting a problem
detailsarray of objectValidation errors only (400 INVALID_REQUEST) - every problem found, with its field path
fieldstringmessagestring
429RATE_LIMITED (retry with backoff), QUOTA_EXCEEDED, DAILY_LIMIT or VIDEOS_PAUSED
{
"message": "Daily limit reached (50 videos per 24 hours); try again later",
"error": "DAILY_LIMIT",
"requestId": "string",
"details": [
{
"field": "edit.clips[0].end",
"message": "80s is past the end of video …, which is 60.0s (1:00); use null for \"to the end\""
}
]
}Schema
messagestringrequiredHuman-readable; wording may change
errorstringrequiredStable machine-readable code (new codes may be added; fall back to the HTTP status)
one of"INVALID_REQUEST","UNAUTHORIZED","MISSING_AUTHENTICATION","FORBIDDEN","REQUEST_REJECTED","NOT_FOUND","ROUTE_NOT_FOUND","METHOD_NOT_ALLOWED","UPLOAD_INCOMPLETE","ALREADY_COMPLETED","ORIGINAL_UNAVAILABLE","EDIT_SOURCE_NOT_READY","NOT_AN_UPLOAD","IDEMPOTENCY_KEY_REUSED","IDEMPOTENCY_IN_PROGRESS","INVALID_STATE","TOO_LARGE","RATE_LIMITED","QUOTA_EXCEEDED","DAILY_LIMIT","VIDEOS_PAUSED","PLAN_UPGRADE_REQUIRED","PLAN_LIMIT_REACHED","INVALID_CODE","TERMS_REQUIRED","SESSION_REQUIRED","ACCOUNT_DISABLED","EMAIL_NOT_CONFIGURED","EMAIL_FAILED","UPSTREAM_ERROR","UNAVAILABLE"requestIdstringrequiredQuote this when reporting a problem
detailsarray of objectValidation errors only (400 INVALID_REQUEST) - every problem found, with its field path
fieldstringmessagestring
503UNAVAILABLE: temporary failure; retry with backoff
{
"message": "Daily limit reached (50 videos per 24 hours); try again later",
"error": "DAILY_LIMIT",
"requestId": "string",
"details": [
{
"field": "edit.clips[0].end",
"message": "80s is past the end of video …, which is 60.0s (1:00); use null for \"to the end\""
}
]
}Schema
messagestringrequiredHuman-readable; wording may change
errorstringrequiredStable machine-readable code (new codes may be added; fall back to the HTTP status)
one of"INVALID_REQUEST","UNAUTHORIZED","MISSING_AUTHENTICATION","FORBIDDEN","REQUEST_REJECTED","NOT_FOUND","ROUTE_NOT_FOUND","METHOD_NOT_ALLOWED","UPLOAD_INCOMPLETE","ALREADY_COMPLETED","ORIGINAL_UNAVAILABLE","EDIT_SOURCE_NOT_READY","NOT_AN_UPLOAD","IDEMPOTENCY_KEY_REUSED","IDEMPOTENCY_IN_PROGRESS","INVALID_STATE","TOO_LARGE","RATE_LIMITED","QUOTA_EXCEEDED","DAILY_LIMIT","VIDEOS_PAUSED","PLAN_UPGRADE_REQUIRED","PLAN_LIMIT_REACHED","INVALID_CODE","TERMS_REQUIRED","SESSION_REQUIRED","ACCOUNT_DISABLED","EMAIL_NOT_CONFIGURED","EMAIL_FAILED","UPSTREAM_ERROR","UNAVAILABLE"requestIdstringrequiredQuote this when reporting a problem
detailsarray of objectValidation errors only (400 INVALID_REQUEST) - every problem found, with its field path
fieldstringmessagestring
operationId getVideo
PATCH/videos/{videoId}Update video metadata (keys to null to remove them)
Parameters
| Name | In | Type | Description |
|---|---|---|---|
videoIdrequired | path | string |
Request body required
{
"metadata": {
"courseId": "algebra-2",
"archived": null
}
}Schema
metadataobjectrequired
Responses
200Updated
{
"videoId": "string",
"metadata": {}
}Schema
videoIdstringmetadataMetadataCustomer key-value pairs (up to 20 keys, 4 KB total). Keys are 1-40 [A-Za-z0-9_.-]; values are strings up to 500 chars.
400INVALID_REQUEST: invalid body or parameter (the message names the field)
{
"message": "Daily limit reached (50 videos per 24 hours); try again later",
"error": "DAILY_LIMIT",
"requestId": "string",
"details": [
{
"field": "edit.clips[0].end",
"message": "80s is past the end of video …, which is 60.0s (1:00); use null for \"to the end\""
}
]
}Schema
messagestringrequiredHuman-readable; wording may change
errorstringrequiredStable machine-readable code (new codes may be added; fall back to the HTTP status)
one of"INVALID_REQUEST","UNAUTHORIZED","MISSING_AUTHENTICATION","FORBIDDEN","REQUEST_REJECTED","NOT_FOUND","ROUTE_NOT_FOUND","METHOD_NOT_ALLOWED","UPLOAD_INCOMPLETE","ALREADY_COMPLETED","ORIGINAL_UNAVAILABLE","EDIT_SOURCE_NOT_READY","NOT_AN_UPLOAD","IDEMPOTENCY_KEY_REUSED","IDEMPOTENCY_IN_PROGRESS","INVALID_STATE","TOO_LARGE","RATE_LIMITED","QUOTA_EXCEEDED","DAILY_LIMIT","VIDEOS_PAUSED","PLAN_UPGRADE_REQUIRED","PLAN_LIMIT_REACHED","INVALID_CODE","TERMS_REQUIRED","SESSION_REQUIRED","ACCOUNT_DISABLED","EMAIL_NOT_CONFIGURED","EMAIL_FAILED","UPSTREAM_ERROR","UNAVAILABLE"requestIdstringrequiredQuote this when reporting a problem
detailsarray of objectValidation errors only (400 INVALID_REQUEST) - every problem found, with its field path
fieldstringmessagestring
401UNAUTHORIZED: missing, unknown or revoked API key
{
"message": "Daily limit reached (50 videos per 24 hours); try again later",
"error": "DAILY_LIMIT",
"requestId": "string",
"details": [
{
"field": "edit.clips[0].end",
"message": "80s is past the end of video …, which is 60.0s (1:00); use null for \"to the end\""
}
]
}Schema
messagestringrequiredHuman-readable; wording may change
errorstringrequiredStable machine-readable code (new codes may be added; fall back to the HTTP status)
one of"INVALID_REQUEST","UNAUTHORIZED","MISSING_AUTHENTICATION","FORBIDDEN","REQUEST_REJECTED","NOT_FOUND","ROUTE_NOT_FOUND","METHOD_NOT_ALLOWED","UPLOAD_INCOMPLETE","ALREADY_COMPLETED","ORIGINAL_UNAVAILABLE","EDIT_SOURCE_NOT_READY","NOT_AN_UPLOAD","IDEMPOTENCY_KEY_REUSED","IDEMPOTENCY_IN_PROGRESS","INVALID_STATE","TOO_LARGE","RATE_LIMITED","QUOTA_EXCEEDED","DAILY_LIMIT","VIDEOS_PAUSED","PLAN_UPGRADE_REQUIRED","PLAN_LIMIT_REACHED","INVALID_CODE","TERMS_REQUIRED","SESSION_REQUIRED","ACCOUNT_DISABLED","EMAIL_NOT_CONFIGURED","EMAIL_FAILED","UPSTREAM_ERROR","UNAVAILABLE"requestIdstringrequiredQuote this when reporting a problem
detailsarray of objectValidation errors only (400 INVALID_REQUEST) - every problem found, with its field path
fieldstringmessagestring
404NOT_FOUND: no such video for this user (wrong ID or someone else's)
{
"message": "Daily limit reached (50 videos per 24 hours); try again later",
"error": "DAILY_LIMIT",
"requestId": "string",
"details": [
{
"field": "edit.clips[0].end",
"message": "80s is past the end of video …, which is 60.0s (1:00); use null for \"to the end\""
}
]
}Schema
messagestringrequiredHuman-readable; wording may change
errorstringrequiredStable machine-readable code (new codes may be added; fall back to the HTTP status)
one of"INVALID_REQUEST","UNAUTHORIZED","MISSING_AUTHENTICATION","FORBIDDEN","REQUEST_REJECTED","NOT_FOUND","ROUTE_NOT_FOUND","METHOD_NOT_ALLOWED","UPLOAD_INCOMPLETE","ALREADY_COMPLETED","ORIGINAL_UNAVAILABLE","EDIT_SOURCE_NOT_READY","NOT_AN_UPLOAD","IDEMPOTENCY_KEY_REUSED","IDEMPOTENCY_IN_PROGRESS","INVALID_STATE","TOO_LARGE","RATE_LIMITED","QUOTA_EXCEEDED","DAILY_LIMIT","VIDEOS_PAUSED","PLAN_UPGRADE_REQUIRED","PLAN_LIMIT_REACHED","INVALID_CODE","TERMS_REQUIRED","SESSION_REQUIRED","ACCOUNT_DISABLED","EMAIL_NOT_CONFIGURED","EMAIL_FAILED","UPSTREAM_ERROR","UNAVAILABLE"requestIdstringrequiredQuote this when reporting a problem
detailsarray of objectValidation errors only (400 INVALID_REQUEST) - every problem found, with its field path
fieldstringmessagestring
409IDEMPOTENCY_KEY_REUSED, IDEMPOTENCY_IN_PROGRESS, or other conflict
{
"message": "Daily limit reached (50 videos per 24 hours); try again later",
"error": "DAILY_LIMIT",
"requestId": "string",
"details": [
{
"field": "edit.clips[0].end",
"message": "80s is past the end of video …, which is 60.0s (1:00); use null for \"to the end\""
}
]
}Schema
messagestringrequiredHuman-readable; wording may change
errorstringrequiredStable machine-readable code (new codes may be added; fall back to the HTTP status)
one of"INVALID_REQUEST","UNAUTHORIZED","MISSING_AUTHENTICATION","FORBIDDEN","REQUEST_REJECTED","NOT_FOUND","ROUTE_NOT_FOUND","METHOD_NOT_ALLOWED","UPLOAD_INCOMPLETE","ALREADY_COMPLETED","ORIGINAL_UNAVAILABLE","EDIT_SOURCE_NOT_READY","NOT_AN_UPLOAD","IDEMPOTENCY_KEY_REUSED","IDEMPOTENCY_IN_PROGRESS","INVALID_STATE","TOO_LARGE","RATE_LIMITED","QUOTA_EXCEEDED","DAILY_LIMIT","VIDEOS_PAUSED","PLAN_UPGRADE_REQUIRED","PLAN_LIMIT_REACHED","INVALID_CODE","TERMS_REQUIRED","SESSION_REQUIRED","ACCOUNT_DISABLED","EMAIL_NOT_CONFIGURED","EMAIL_FAILED","UPSTREAM_ERROR","UNAVAILABLE"requestIdstringrequiredQuote this when reporting a problem
detailsarray of objectValidation errors only (400 INVALID_REQUEST) - every problem found, with its field path
fieldstringmessagestring
429RATE_LIMITED (retry with backoff), QUOTA_EXCEEDED, DAILY_LIMIT or VIDEOS_PAUSED
{
"message": "Daily limit reached (50 videos per 24 hours); try again later",
"error": "DAILY_LIMIT",
"requestId": "string",
"details": [
{
"field": "edit.clips[0].end",
"message": "80s is past the end of video …, which is 60.0s (1:00); use null for \"to the end\""
}
]
}Schema
messagestringrequiredHuman-readable; wording may change
errorstringrequiredStable machine-readable code (new codes may be added; fall back to the HTTP status)
one of"INVALID_REQUEST","UNAUTHORIZED","MISSING_AUTHENTICATION","FORBIDDEN","REQUEST_REJECTED","NOT_FOUND","ROUTE_NOT_FOUND","METHOD_NOT_ALLOWED","UPLOAD_INCOMPLETE","ALREADY_COMPLETED","ORIGINAL_UNAVAILABLE","EDIT_SOURCE_NOT_READY","NOT_AN_UPLOAD","IDEMPOTENCY_KEY_REUSED","IDEMPOTENCY_IN_PROGRESS","INVALID_STATE","TOO_LARGE","RATE_LIMITED","QUOTA_EXCEEDED","DAILY_LIMIT","VIDEOS_PAUSED","PLAN_UPGRADE_REQUIRED","PLAN_LIMIT_REACHED","INVALID_CODE","TERMS_REQUIRED","SESSION_REQUIRED","ACCOUNT_DISABLED","EMAIL_NOT_CONFIGURED","EMAIL_FAILED","UPSTREAM_ERROR","UNAVAILABLE"requestIdstringrequiredQuote this when reporting a problem
detailsarray of objectValidation errors only (400 INVALID_REQUEST) - every problem found, with its field path
fieldstringmessagestring
503UNAVAILABLE: temporary failure; retry with backoff
{
"message": "Daily limit reached (50 videos per 24 hours); try again later",
"error": "DAILY_LIMIT",
"requestId": "string",
"details": [
{
"field": "edit.clips[0].end",
"message": "80s is past the end of video …, which is 60.0s (1:00); use null for \"to the end\""
}
]
}Schema
messagestringrequiredHuman-readable; wording may change
errorstringrequiredStable machine-readable code (new codes may be added; fall back to the HTTP status)
one of"INVALID_REQUEST","UNAUTHORIZED","MISSING_AUTHENTICATION","FORBIDDEN","REQUEST_REJECTED","NOT_FOUND","ROUTE_NOT_FOUND","METHOD_NOT_ALLOWED","UPLOAD_INCOMPLETE","ALREADY_COMPLETED","ORIGINAL_UNAVAILABLE","EDIT_SOURCE_NOT_READY","NOT_AN_UPLOAD","IDEMPOTENCY_KEY_REUSED","IDEMPOTENCY_IN_PROGRESS","INVALID_STATE","TOO_LARGE","RATE_LIMITED","QUOTA_EXCEEDED","DAILY_LIMIT","VIDEOS_PAUSED","PLAN_UPGRADE_REQUIRED","PLAN_LIMIT_REACHED","INVALID_CODE","TERMS_REQUIRED","SESSION_REQUIRED","ACCOUNT_DISABLED","EMAIL_NOT_CONFIGURED","EMAIL_FAILED","UPSTREAM_ERROR","UNAVAILABLE"requestIdstringrequiredQuote this when reporting a problem
detailsarray of objectValidation errors only (400 INVALID_REQUEST) - every problem found, with its field path
fieldstringmessagestring
operationId updateVideo
DELETE/videos/{videoId}Remove a video (irreversible; stops billing its storage)
Cancels any running processing, removes outputs and work files. The original upload is removed unless another video was reprocessed from it and still exists. Usage records are kept for billing history.
Parameters
| Name | In | Type | Description |
|---|---|---|---|
videoIdrequired | path | string |
Responses
200Deleted
{
"videoId": "string",
"deleted": true,
"originalDeleted": false,
"freedBytes": 0
}Schema
videoIdstringdeletedbooleanoriginalDeletedbooleanWhether the original upload was also removed
freedBytesintegerTotal bytes removed from storage
400INVALID_REQUEST: invalid body or parameter (the message names the field)
{
"message": "Daily limit reached (50 videos per 24 hours); try again later",
"error": "DAILY_LIMIT",
"requestId": "string",
"details": [
{
"field": "edit.clips[0].end",
"message": "80s is past the end of video …, which is 60.0s (1:00); use null for \"to the end\""
}
]
}Schema
messagestringrequiredHuman-readable; wording may change
errorstringrequiredStable machine-readable code (new codes may be added; fall back to the HTTP status)
one of"INVALID_REQUEST","UNAUTHORIZED","MISSING_AUTHENTICATION","FORBIDDEN","REQUEST_REJECTED","NOT_FOUND","ROUTE_NOT_FOUND","METHOD_NOT_ALLOWED","UPLOAD_INCOMPLETE","ALREADY_COMPLETED","ORIGINAL_UNAVAILABLE","EDIT_SOURCE_NOT_READY","NOT_AN_UPLOAD","IDEMPOTENCY_KEY_REUSED","IDEMPOTENCY_IN_PROGRESS","INVALID_STATE","TOO_LARGE","RATE_LIMITED","QUOTA_EXCEEDED","DAILY_LIMIT","VIDEOS_PAUSED","PLAN_UPGRADE_REQUIRED","PLAN_LIMIT_REACHED","INVALID_CODE","TERMS_REQUIRED","SESSION_REQUIRED","ACCOUNT_DISABLED","EMAIL_NOT_CONFIGURED","EMAIL_FAILED","UPSTREAM_ERROR","UNAVAILABLE"requestIdstringrequiredQuote this when reporting a problem
detailsarray of objectValidation errors only (400 INVALID_REQUEST) - every problem found, with its field path
fieldstringmessagestring
401UNAUTHORIZED: missing, unknown or revoked API key
{
"message": "Daily limit reached (50 videos per 24 hours); try again later",
"error": "DAILY_LIMIT",
"requestId": "string",
"details": [
{
"field": "edit.clips[0].end",
"message": "80s is past the end of video …, which is 60.0s (1:00); use null for \"to the end\""
}
]
}Schema
messagestringrequiredHuman-readable; wording may change
errorstringrequiredStable machine-readable code (new codes may be added; fall back to the HTTP status)
one of"INVALID_REQUEST","UNAUTHORIZED","MISSING_AUTHENTICATION","FORBIDDEN","REQUEST_REJECTED","NOT_FOUND","ROUTE_NOT_FOUND","METHOD_NOT_ALLOWED","UPLOAD_INCOMPLETE","ALREADY_COMPLETED","ORIGINAL_UNAVAILABLE","EDIT_SOURCE_NOT_READY","NOT_AN_UPLOAD","IDEMPOTENCY_KEY_REUSED","IDEMPOTENCY_IN_PROGRESS","INVALID_STATE","TOO_LARGE","RATE_LIMITED","QUOTA_EXCEEDED","DAILY_LIMIT","VIDEOS_PAUSED","PLAN_UPGRADE_REQUIRED","PLAN_LIMIT_REACHED","INVALID_CODE","TERMS_REQUIRED","SESSION_REQUIRED","ACCOUNT_DISABLED","EMAIL_NOT_CONFIGURED","EMAIL_FAILED","UPSTREAM_ERROR","UNAVAILABLE"requestIdstringrequiredQuote this when reporting a problem
detailsarray of objectValidation errors only (400 INVALID_REQUEST) - every problem found, with its field path
fieldstringmessagestring
404NOT_FOUND: no such video for this user (wrong ID or someone else's)
{
"message": "Daily limit reached (50 videos per 24 hours); try again later",
"error": "DAILY_LIMIT",
"requestId": "string",
"details": [
{
"field": "edit.clips[0].end",
"message": "80s is past the end of video …, which is 60.0s (1:00); use null for \"to the end\""
}
]
}Schema
messagestringrequiredHuman-readable; wording may change
errorstringrequiredStable machine-readable code (new codes may be added; fall back to the HTTP status)
one of"INVALID_REQUEST","UNAUTHORIZED","MISSING_AUTHENTICATION","FORBIDDEN","REQUEST_REJECTED","NOT_FOUND","ROUTE_NOT_FOUND","METHOD_NOT_ALLOWED","UPLOAD_INCOMPLETE","ALREADY_COMPLETED","ORIGINAL_UNAVAILABLE","EDIT_SOURCE_NOT_READY","NOT_AN_UPLOAD","IDEMPOTENCY_KEY_REUSED","IDEMPOTENCY_IN_PROGRESS","INVALID_STATE","TOO_LARGE","RATE_LIMITED","QUOTA_EXCEEDED","DAILY_LIMIT","VIDEOS_PAUSED","PLAN_UPGRADE_REQUIRED","PLAN_LIMIT_REACHED","INVALID_CODE","TERMS_REQUIRED","SESSION_REQUIRED","ACCOUNT_DISABLED","EMAIL_NOT_CONFIGURED","EMAIL_FAILED","UPSTREAM_ERROR","UNAVAILABLE"requestIdstringrequiredQuote this when reporting a problem
detailsarray of objectValidation errors only (400 INVALID_REQUEST) - every problem found, with its field path
fieldstringmessagestring
429RATE_LIMITED (retry with backoff), QUOTA_EXCEEDED, DAILY_LIMIT or VIDEOS_PAUSED
{
"message": "Daily limit reached (50 videos per 24 hours); try again later",
"error": "DAILY_LIMIT",
"requestId": "string",
"details": [
{
"field": "edit.clips[0].end",
"message": "80s is past the end of video …, which is 60.0s (1:00); use null for \"to the end\""
}
]
}Schema
messagestringrequiredHuman-readable; wording may change
errorstringrequiredStable machine-readable code (new codes may be added; fall back to the HTTP status)
one of"INVALID_REQUEST","UNAUTHORIZED","MISSING_AUTHENTICATION","FORBIDDEN","REQUEST_REJECTED","NOT_FOUND","ROUTE_NOT_FOUND","METHOD_NOT_ALLOWED","UPLOAD_INCOMPLETE","ALREADY_COMPLETED","ORIGINAL_UNAVAILABLE","EDIT_SOURCE_NOT_READY","NOT_AN_UPLOAD","IDEMPOTENCY_KEY_REUSED","IDEMPOTENCY_IN_PROGRESS","INVALID_STATE","TOO_LARGE","RATE_LIMITED","QUOTA_EXCEEDED","DAILY_LIMIT","VIDEOS_PAUSED","PLAN_UPGRADE_REQUIRED","PLAN_LIMIT_REACHED","INVALID_CODE","TERMS_REQUIRED","SESSION_REQUIRED","ACCOUNT_DISABLED","EMAIL_NOT_CONFIGURED","EMAIL_FAILED","UPSTREAM_ERROR","UNAVAILABLE"requestIdstringrequiredQuote this when reporting a problem
detailsarray of objectValidation errors only (400 INVALID_REQUEST) - every problem found, with its field path
fieldstringmessagestring
503UNAVAILABLE: temporary failure; retry with backoff
{
"message": "Daily limit reached (50 videos per 24 hours); try again later",
"error": "DAILY_LIMIT",
"requestId": "string",
"details": [
{
"field": "edit.clips[0].end",
"message": "80s is past the end of video …, which is 60.0s (1:00); use null for \"to the end\""
}
]
}Schema
messagestringrequiredHuman-readable; wording may change
errorstringrequiredStable machine-readable code (new codes may be added; fall back to the HTTP status)
one of"INVALID_REQUEST","UNAUTHORIZED","MISSING_AUTHENTICATION","FORBIDDEN","REQUEST_REJECTED","NOT_FOUND","ROUTE_NOT_FOUND","METHOD_NOT_ALLOWED","UPLOAD_INCOMPLETE","ALREADY_COMPLETED","ORIGINAL_UNAVAILABLE","EDIT_SOURCE_NOT_READY","NOT_AN_UPLOAD","IDEMPOTENCY_KEY_REUSED","IDEMPOTENCY_IN_PROGRESS","INVALID_STATE","TOO_LARGE","RATE_LIMITED","QUOTA_EXCEEDED","DAILY_LIMIT","VIDEOS_PAUSED","PLAN_UPGRADE_REQUIRED","PLAN_LIMIT_REACHED","INVALID_CODE","TERMS_REQUIRED","SESSION_REQUIRED","ACCOUNT_DISABLED","EMAIL_NOT_CONFIGURED","EMAIL_FAILED","UPSTREAM_ERROR","UNAVAILABLE"requestIdstringrequiredQuote this when reporting a problem
detailsarray of objectValidation errors only (400 INVALID_REQUEST) - every problem found, with its field path
fieldstringmessagestring
operationId deleteVideo
Uploads
Send a file's bytes in parts with pre-signed URLs, then complete or abort the upload.
POST/videos/{videoId}/partsGet pre-signed upload URLs for parts
Parameters
| Name | In | Type | Description |
|---|---|---|---|
videoIdrequired | path | string |
Request body required
{
"partNumbers": [
1,
2
],
"file": "source",
"parts": [
{
"partNumber": 0,
"sizeBytes": 0
}
]
}Schema
partNumbersarray of integerFor normal uploads (1-100 per call)
filestringone of"source","camera"· default"source"partsarray of objectFor streaming uploads, each part with its exact size (>= 5 MiB except the last)
partNumberintegersizeBytesinteger
Responses
200URLs valid for 15 minutes; PUT the bytes with exactly sizeBytes as Content-Length
{
"parts": [
{
"partNumber": 0,
"sizeBytes": 0,
"url": "string"
}
],
"expiresInSeconds": 0
}Schema
partsarray of objectpartNumberintegersizeBytesintegerurlstring
expiresInSecondsinteger
400INVALID_REQUEST: invalid body or parameter (the message names the field)
{
"message": "Daily limit reached (50 videos per 24 hours); try again later",
"error": "DAILY_LIMIT",
"requestId": "string",
"details": [
{
"field": "edit.clips[0].end",
"message": "80s is past the end of video …, which is 60.0s (1:00); use null for \"to the end\""
}
]
}Schema
messagestringrequiredHuman-readable; wording may change
errorstringrequiredStable machine-readable code (new codes may be added; fall back to the HTTP status)
one of"INVALID_REQUEST","UNAUTHORIZED","MISSING_AUTHENTICATION","FORBIDDEN","REQUEST_REJECTED","NOT_FOUND","ROUTE_NOT_FOUND","METHOD_NOT_ALLOWED","UPLOAD_INCOMPLETE","ALREADY_COMPLETED","ORIGINAL_UNAVAILABLE","EDIT_SOURCE_NOT_READY","NOT_AN_UPLOAD","IDEMPOTENCY_KEY_REUSED","IDEMPOTENCY_IN_PROGRESS","INVALID_STATE","TOO_LARGE","RATE_LIMITED","QUOTA_EXCEEDED","DAILY_LIMIT","VIDEOS_PAUSED","PLAN_UPGRADE_REQUIRED","PLAN_LIMIT_REACHED","INVALID_CODE","TERMS_REQUIRED","SESSION_REQUIRED","ACCOUNT_DISABLED","EMAIL_NOT_CONFIGURED","EMAIL_FAILED","UPSTREAM_ERROR","UNAVAILABLE"requestIdstringrequiredQuote this when reporting a problem
detailsarray of objectValidation errors only (400 INVALID_REQUEST) - every problem found, with its field path
fieldstringmessagestring
401UNAUTHORIZED: missing, unknown or revoked API key
{
"message": "Daily limit reached (50 videos per 24 hours); try again later",
"error": "DAILY_LIMIT",
"requestId": "string",
"details": [
{
"field": "edit.clips[0].end",
"message": "80s is past the end of video …, which is 60.0s (1:00); use null for \"to the end\""
}
]
}Schema
messagestringrequiredHuman-readable; wording may change
errorstringrequiredStable machine-readable code (new codes may be added; fall back to the HTTP status)
one of"INVALID_REQUEST","UNAUTHORIZED","MISSING_AUTHENTICATION","FORBIDDEN","REQUEST_REJECTED","NOT_FOUND","ROUTE_NOT_FOUND","METHOD_NOT_ALLOWED","UPLOAD_INCOMPLETE","ALREADY_COMPLETED","ORIGINAL_UNAVAILABLE","EDIT_SOURCE_NOT_READY","NOT_AN_UPLOAD","IDEMPOTENCY_KEY_REUSED","IDEMPOTENCY_IN_PROGRESS","INVALID_STATE","TOO_LARGE","RATE_LIMITED","QUOTA_EXCEEDED","DAILY_LIMIT","VIDEOS_PAUSED","PLAN_UPGRADE_REQUIRED","PLAN_LIMIT_REACHED","INVALID_CODE","TERMS_REQUIRED","SESSION_REQUIRED","ACCOUNT_DISABLED","EMAIL_NOT_CONFIGURED","EMAIL_FAILED","UPSTREAM_ERROR","UNAVAILABLE"requestIdstringrequiredQuote this when reporting a problem
detailsarray of objectValidation errors only (400 INVALID_REQUEST) - every problem found, with its field path
fieldstringmessagestring
404NOT_FOUND: no such video for this user (wrong ID or someone else's)
{
"message": "Daily limit reached (50 videos per 24 hours); try again later",
"error": "DAILY_LIMIT",
"requestId": "string",
"details": [
{
"field": "edit.clips[0].end",
"message": "80s is past the end of video …, which is 60.0s (1:00); use null for \"to the end\""
}
]
}Schema
messagestringrequiredHuman-readable; wording may change
errorstringrequiredStable machine-readable code (new codes may be added; fall back to the HTTP status)
one of"INVALID_REQUEST","UNAUTHORIZED","MISSING_AUTHENTICATION","FORBIDDEN","REQUEST_REJECTED","NOT_FOUND","ROUTE_NOT_FOUND","METHOD_NOT_ALLOWED","UPLOAD_INCOMPLETE","ALREADY_COMPLETED","ORIGINAL_UNAVAILABLE","EDIT_SOURCE_NOT_READY","NOT_AN_UPLOAD","IDEMPOTENCY_KEY_REUSED","IDEMPOTENCY_IN_PROGRESS","INVALID_STATE","TOO_LARGE","RATE_LIMITED","QUOTA_EXCEEDED","DAILY_LIMIT","VIDEOS_PAUSED","PLAN_UPGRADE_REQUIRED","PLAN_LIMIT_REACHED","INVALID_CODE","TERMS_REQUIRED","SESSION_REQUIRED","ACCOUNT_DISABLED","EMAIL_NOT_CONFIGURED","EMAIL_FAILED","UPSTREAM_ERROR","UNAVAILABLE"requestIdstringrequiredQuote this when reporting a problem
detailsarray of objectValidation errors only (400 INVALID_REQUEST) - every problem found, with its field path
fieldstringmessagestring
429RATE_LIMITED (retry with backoff), QUOTA_EXCEEDED, DAILY_LIMIT or VIDEOS_PAUSED
{
"message": "Daily limit reached (50 videos per 24 hours); try again later",
"error": "DAILY_LIMIT",
"requestId": "string",
"details": [
{
"field": "edit.clips[0].end",
"message": "80s is past the end of video …, which is 60.0s (1:00); use null for \"to the end\""
}
]
}Schema
messagestringrequiredHuman-readable; wording may change
errorstringrequiredStable machine-readable code (new codes may be added; fall back to the HTTP status)
one of"INVALID_REQUEST","UNAUTHORIZED","MISSING_AUTHENTICATION","FORBIDDEN","REQUEST_REJECTED","NOT_FOUND","ROUTE_NOT_FOUND","METHOD_NOT_ALLOWED","UPLOAD_INCOMPLETE","ALREADY_COMPLETED","ORIGINAL_UNAVAILABLE","EDIT_SOURCE_NOT_READY","NOT_AN_UPLOAD","IDEMPOTENCY_KEY_REUSED","IDEMPOTENCY_IN_PROGRESS","INVALID_STATE","TOO_LARGE","RATE_LIMITED","QUOTA_EXCEEDED","DAILY_LIMIT","VIDEOS_PAUSED","PLAN_UPGRADE_REQUIRED","PLAN_LIMIT_REACHED","INVALID_CODE","TERMS_REQUIRED","SESSION_REQUIRED","ACCOUNT_DISABLED","EMAIL_NOT_CONFIGURED","EMAIL_FAILED","UPSTREAM_ERROR","UNAVAILABLE"requestIdstringrequiredQuote this when reporting a problem
detailsarray of objectValidation errors only (400 INVALID_REQUEST) - every problem found, with its field path
fieldstringmessagestring
503UNAVAILABLE: temporary failure; retry with backoff
{
"message": "Daily limit reached (50 videos per 24 hours); try again later",
"error": "DAILY_LIMIT",
"requestId": "string",
"details": [
{
"field": "edit.clips[0].end",
"message": "80s is past the end of video …, which is 60.0s (1:00); use null for \"to the end\""
}
]
}Schema
messagestringrequiredHuman-readable; wording may change
errorstringrequiredStable machine-readable code (new codes may be added; fall back to the HTTP status)
one of"INVALID_REQUEST","UNAUTHORIZED","MISSING_AUTHENTICATION","FORBIDDEN","REQUEST_REJECTED","NOT_FOUND","ROUTE_NOT_FOUND","METHOD_NOT_ALLOWED","UPLOAD_INCOMPLETE","ALREADY_COMPLETED","ORIGINAL_UNAVAILABLE","EDIT_SOURCE_NOT_READY","NOT_AN_UPLOAD","IDEMPOTENCY_KEY_REUSED","IDEMPOTENCY_IN_PROGRESS","INVALID_STATE","TOO_LARGE","RATE_LIMITED","QUOTA_EXCEEDED","DAILY_LIMIT","VIDEOS_PAUSED","PLAN_UPGRADE_REQUIRED","PLAN_LIMIT_REACHED","INVALID_CODE","TERMS_REQUIRED","SESSION_REQUIRED","ACCOUNT_DISABLED","EMAIL_NOT_CONFIGURED","EMAIL_FAILED","UPSTREAM_ERROR","UNAVAILABLE"requestIdstringrequiredQuote this when reporting a problem
detailsarray of objectValidation errors only (400 INVALID_REQUEST) - every problem found, with its field path
fieldstringmessagestring
operationId getPartUrls
POST/videos/{videoId}/completeFinish the upload and start processing (optionally with redactions)
Parameters
| Name | In | Type | Description |
|---|---|---|---|
dryRun | query | boolean | Validate only: nothing is created or started (answers 200 with valid: true) |
videoIdrequired | path | string | |
Idempotency-Key | header | string | Unique key for idempotent requests: POST /videos, POST /videos/{id}/complete, POST /videos/{id}/reprocess. Same key with same request = replay of first response. Stored 24 hours. |
Request body
{
"redactions": [
{
"type": "blur",
"shape": "rect",
"x": 0,
"y": 0,
"w": 0,
"h": 0,
"start": 0,
"end": 0,
"track": [
{
"time": 0,
"x": 0,
"y": 0,
"w": 0,
"h": 0
}
]
}
],
"deleteOriginal": false,
"trim": {
"start": 0,
"end": 0
},
"source": {
"sizeBytes": 0,
"partCount": 0
},
"camera": {
"sizeBytes": 0,
"partCount": 0
}
}Schema
redactionsarray of Redactionmax items20typestringrequiredbox = solid black bar
one of"blur","box"shapestringone of"rect","ellipse"· default"rect"xnumberFraction of the final frame width
min0· max1ynumbermin0· max1wnumbermin0· max1hnumbermin0· max1startnumberSeconds
default0endnumberSeconds; null = until the end
trackarray of objectKeyframes for moving content, in time order; the region glides between them (instead of x, y, w, h)
max items50timenumberrequiredSeconds on the output timeline
min0xnumberrequiredmin0· max1ynumberrequiredmin0· max1wnumberrequiredmin0· max1hnumberrequiredmin0· max1
deleteOriginalbooleanDelete the unredacted upload after the output is published
trimTrimKeep only part of the upload (frame-accurate, no extra encode). Redaction times are on the trimmed timeline.
startnumbermin0· default0endnumbernull = to the end
sourceobjectStreaming uploads only
sizeBytesintegerpartCountinteger
cameraobjectStreaming uploads only
sizeBytesintegerpartCountinteger
Responses
202Accepted; poll GET /videos/{videoId}
400INVALID_REQUEST: invalid body or parameter (the message names the field)
{
"message": "Daily limit reached (50 videos per 24 hours); try again later",
"error": "DAILY_LIMIT",
"requestId": "string",
"details": [
{
"field": "edit.clips[0].end",
"message": "80s is past the end of video …, which is 60.0s (1:00); use null for \"to the end\""
}
]
}Schema
messagestringrequiredHuman-readable; wording may change
errorstringrequiredStable machine-readable code (new codes may be added; fall back to the HTTP status)
one of"INVALID_REQUEST","UNAUTHORIZED","MISSING_AUTHENTICATION","FORBIDDEN","REQUEST_REJECTED","NOT_FOUND","ROUTE_NOT_FOUND","METHOD_NOT_ALLOWED","UPLOAD_INCOMPLETE","ALREADY_COMPLETED","ORIGINAL_UNAVAILABLE","EDIT_SOURCE_NOT_READY","NOT_AN_UPLOAD","IDEMPOTENCY_KEY_REUSED","IDEMPOTENCY_IN_PROGRESS","INVALID_STATE","TOO_LARGE","RATE_LIMITED","QUOTA_EXCEEDED","DAILY_LIMIT","VIDEOS_PAUSED","PLAN_UPGRADE_REQUIRED","PLAN_LIMIT_REACHED","INVALID_CODE","TERMS_REQUIRED","SESSION_REQUIRED","ACCOUNT_DISABLED","EMAIL_NOT_CONFIGURED","EMAIL_FAILED","UPSTREAM_ERROR","UNAVAILABLE"requestIdstringrequiredQuote this when reporting a problem
detailsarray of objectValidation errors only (400 INVALID_REQUEST) - every problem found, with its field path
fieldstringmessagestring
401UNAUTHORIZED: missing, unknown or revoked API key
{
"message": "Daily limit reached (50 videos per 24 hours); try again later",
"error": "DAILY_LIMIT",
"requestId": "string",
"details": [
{
"field": "edit.clips[0].end",
"message": "80s is past the end of video …, which is 60.0s (1:00); use null for \"to the end\""
}
]
}Schema
messagestringrequiredHuman-readable; wording may change
errorstringrequiredStable machine-readable code (new codes may be added; fall back to the HTTP status)
one of"INVALID_REQUEST","UNAUTHORIZED","MISSING_AUTHENTICATION","FORBIDDEN","REQUEST_REJECTED","NOT_FOUND","ROUTE_NOT_FOUND","METHOD_NOT_ALLOWED","UPLOAD_INCOMPLETE","ALREADY_COMPLETED","ORIGINAL_UNAVAILABLE","EDIT_SOURCE_NOT_READY","NOT_AN_UPLOAD","IDEMPOTENCY_KEY_REUSED","IDEMPOTENCY_IN_PROGRESS","INVALID_STATE","TOO_LARGE","RATE_LIMITED","QUOTA_EXCEEDED","DAILY_LIMIT","VIDEOS_PAUSED","PLAN_UPGRADE_REQUIRED","PLAN_LIMIT_REACHED","INVALID_CODE","TERMS_REQUIRED","SESSION_REQUIRED","ACCOUNT_DISABLED","EMAIL_NOT_CONFIGURED","EMAIL_FAILED","UPSTREAM_ERROR","UNAVAILABLE"requestIdstringrequiredQuote this when reporting a problem
detailsarray of objectValidation errors only (400 INVALID_REQUEST) - every problem found, with its field path
fieldstringmessagestring
403Plan does not allow this feature (e.g. redaction) or plan limit reached (free tier only)
{
"message": "Daily limit reached (50 videos per 24 hours); try again later",
"error": "DAILY_LIMIT",
"requestId": "string",
"details": [
{
"field": "edit.clips[0].end",
"message": "80s is past the end of video …, which is 60.0s (1:00); use null for \"to the end\""
}
]
}Schema
messagestringrequiredHuman-readable; wording may change
errorstringrequiredStable machine-readable code (new codes may be added; fall back to the HTTP status)
one of"INVALID_REQUEST","UNAUTHORIZED","MISSING_AUTHENTICATION","FORBIDDEN","REQUEST_REJECTED","NOT_FOUND","ROUTE_NOT_FOUND","METHOD_NOT_ALLOWED","UPLOAD_INCOMPLETE","ALREADY_COMPLETED","ORIGINAL_UNAVAILABLE","EDIT_SOURCE_NOT_READY","NOT_AN_UPLOAD","IDEMPOTENCY_KEY_REUSED","IDEMPOTENCY_IN_PROGRESS","INVALID_STATE","TOO_LARGE","RATE_LIMITED","QUOTA_EXCEEDED","DAILY_LIMIT","VIDEOS_PAUSED","PLAN_UPGRADE_REQUIRED","PLAN_LIMIT_REACHED","INVALID_CODE","TERMS_REQUIRED","SESSION_REQUIRED","ACCOUNT_DISABLED","EMAIL_NOT_CONFIGURED","EMAIL_FAILED","UPSTREAM_ERROR","UNAVAILABLE"requestIdstringrequiredQuote this when reporting a problem
detailsarray of objectValidation errors only (400 INVALID_REQUEST) - every problem found, with its field path
fieldstringmessagestring
404NOT_FOUND: no such video for this user (wrong ID or someone else's)
{
"message": "Daily limit reached (50 videos per 24 hours); try again later",
"error": "DAILY_LIMIT",
"requestId": "string",
"details": [
{
"field": "edit.clips[0].end",
"message": "80s is past the end of video …, which is 60.0s (1:00); use null for \"to the end\""
}
]
}Schema
messagestringrequiredHuman-readable; wording may change
errorstringrequiredStable machine-readable code (new codes may be added; fall back to the HTTP status)
one of"INVALID_REQUEST","UNAUTHORIZED","MISSING_AUTHENTICATION","FORBIDDEN","REQUEST_REJECTED","NOT_FOUND","ROUTE_NOT_FOUND","METHOD_NOT_ALLOWED","UPLOAD_INCOMPLETE","ALREADY_COMPLETED","ORIGINAL_UNAVAILABLE","EDIT_SOURCE_NOT_READY","NOT_AN_UPLOAD","IDEMPOTENCY_KEY_REUSED","IDEMPOTENCY_IN_PROGRESS","INVALID_STATE","TOO_LARGE","RATE_LIMITED","QUOTA_EXCEEDED","DAILY_LIMIT","VIDEOS_PAUSED","PLAN_UPGRADE_REQUIRED","PLAN_LIMIT_REACHED","INVALID_CODE","TERMS_REQUIRED","SESSION_REQUIRED","ACCOUNT_DISABLED","EMAIL_NOT_CONFIGURED","EMAIL_FAILED","UPSTREAM_ERROR","UNAVAILABLE"requestIdstringrequiredQuote this when reporting a problem
detailsarray of objectValidation errors only (400 INVALID_REQUEST) - every problem found, with its field path
fieldstringmessagestring
409Upload incomplete or sizes don't match
{
"message": "Daily limit reached (50 videos per 24 hours); try again later",
"error": "DAILY_LIMIT",
"requestId": "string",
"details": [
{
"field": "edit.clips[0].end",
"message": "80s is past the end of video …, which is 60.0s (1:00); use null for \"to the end\""
}
]
}Schema
messagestringrequiredHuman-readable; wording may change
errorstringrequiredStable machine-readable code (new codes may be added; fall back to the HTTP status)
one of"INVALID_REQUEST","UNAUTHORIZED","MISSING_AUTHENTICATION","FORBIDDEN","REQUEST_REJECTED","NOT_FOUND","ROUTE_NOT_FOUND","METHOD_NOT_ALLOWED","UPLOAD_INCOMPLETE","ALREADY_COMPLETED","ORIGINAL_UNAVAILABLE","EDIT_SOURCE_NOT_READY","NOT_AN_UPLOAD","IDEMPOTENCY_KEY_REUSED","IDEMPOTENCY_IN_PROGRESS","INVALID_STATE","TOO_LARGE","RATE_LIMITED","QUOTA_EXCEEDED","DAILY_LIMIT","VIDEOS_PAUSED","PLAN_UPGRADE_REQUIRED","PLAN_LIMIT_REACHED","INVALID_CODE","TERMS_REQUIRED","SESSION_REQUIRED","ACCOUNT_DISABLED","EMAIL_NOT_CONFIGURED","EMAIL_FAILED","UPSTREAM_ERROR","UNAVAILABLE"requestIdstringrequiredQuote this when reporting a problem
detailsarray of objectValidation errors only (400 INVALID_REQUEST) - every problem found, with its field path
fieldstringmessagestring
429RATE_LIMITED (retry with backoff), QUOTA_EXCEEDED, DAILY_LIMIT or VIDEOS_PAUSED
{
"message": "Daily limit reached (50 videos per 24 hours); try again later",
"error": "DAILY_LIMIT",
"requestId": "string",
"details": [
{
"field": "edit.clips[0].end",
"message": "80s is past the end of video …, which is 60.0s (1:00); use null for \"to the end\""
}
]
}Schema
messagestringrequiredHuman-readable; wording may change
errorstringrequiredStable machine-readable code (new codes may be added; fall back to the HTTP status)
one of"INVALID_REQUEST","UNAUTHORIZED","MISSING_AUTHENTICATION","FORBIDDEN","REQUEST_REJECTED","NOT_FOUND","ROUTE_NOT_FOUND","METHOD_NOT_ALLOWED","UPLOAD_INCOMPLETE","ALREADY_COMPLETED","ORIGINAL_UNAVAILABLE","EDIT_SOURCE_NOT_READY","NOT_AN_UPLOAD","IDEMPOTENCY_KEY_REUSED","IDEMPOTENCY_IN_PROGRESS","INVALID_STATE","TOO_LARGE","RATE_LIMITED","QUOTA_EXCEEDED","DAILY_LIMIT","VIDEOS_PAUSED","PLAN_UPGRADE_REQUIRED","PLAN_LIMIT_REACHED","INVALID_CODE","TERMS_REQUIRED","SESSION_REQUIRED","ACCOUNT_DISABLED","EMAIL_NOT_CONFIGURED","EMAIL_FAILED","UPSTREAM_ERROR","UNAVAILABLE"requestIdstringrequiredQuote this when reporting a problem
detailsarray of objectValidation errors only (400 INVALID_REQUEST) - every problem found, with its field path
fieldstringmessagestring
503UNAVAILABLE: temporary failure; retry with backoff
{
"message": "Daily limit reached (50 videos per 24 hours); try again later",
"error": "DAILY_LIMIT",
"requestId": "string",
"details": [
{
"field": "edit.clips[0].end",
"message": "80s is past the end of video …, which is 60.0s (1:00); use null for \"to the end\""
}
]
}Schema
messagestringrequiredHuman-readable; wording may change
errorstringrequiredStable machine-readable code (new codes may be added; fall back to the HTTP status)
one of"INVALID_REQUEST","UNAUTHORIZED","MISSING_AUTHENTICATION","FORBIDDEN","REQUEST_REJECTED","NOT_FOUND","ROUTE_NOT_FOUND","METHOD_NOT_ALLOWED","UPLOAD_INCOMPLETE","ALREADY_COMPLETED","ORIGINAL_UNAVAILABLE","EDIT_SOURCE_NOT_READY","NOT_AN_UPLOAD","IDEMPOTENCY_KEY_REUSED","IDEMPOTENCY_IN_PROGRESS","INVALID_STATE","TOO_LARGE","RATE_LIMITED","QUOTA_EXCEEDED","DAILY_LIMIT","VIDEOS_PAUSED","PLAN_UPGRADE_REQUIRED","PLAN_LIMIT_REACHED","INVALID_CODE","TERMS_REQUIRED","SESSION_REQUIRED","ACCOUNT_DISABLED","EMAIL_NOT_CONFIGURED","EMAIL_FAILED","UPSTREAM_ERROR","UNAVAILABLE"requestIdstringrequiredQuote this when reporting a problem
detailsarray of objectValidation errors only (400 INVALID_REQUEST) - every problem found, with its field path
fieldstringmessagestring
operationId completeUpload
POST/videos/{videoId}/abortCancel an unfinished upload (deletes uploaded parts and the record)
Parameters
| Name | In | Type | Description |
|---|---|---|---|
videoIdrequired | path | string |
Responses
200Cancelled
409Upload already completed
{
"message": "Daily limit reached (50 videos per 24 hours); try again later",
"error": "DAILY_LIMIT",
"requestId": "string",
"details": [
{
"field": "edit.clips[0].end",
"message": "80s is past the end of video …, which is 60.0s (1:00); use null for \"to the end\""
}
]
}Schema
messagestringrequiredHuman-readable; wording may change
errorstringrequiredStable machine-readable code (new codes may be added; fall back to the HTTP status)
one of"INVALID_REQUEST","UNAUTHORIZED","MISSING_AUTHENTICATION","FORBIDDEN","REQUEST_REJECTED","NOT_FOUND","ROUTE_NOT_FOUND","METHOD_NOT_ALLOWED","UPLOAD_INCOMPLETE","ALREADY_COMPLETED","ORIGINAL_UNAVAILABLE","EDIT_SOURCE_NOT_READY","NOT_AN_UPLOAD","IDEMPOTENCY_KEY_REUSED","IDEMPOTENCY_IN_PROGRESS","INVALID_STATE","TOO_LARGE","RATE_LIMITED","QUOTA_EXCEEDED","DAILY_LIMIT","VIDEOS_PAUSED","PLAN_UPGRADE_REQUIRED","PLAN_LIMIT_REACHED","INVALID_CODE","TERMS_REQUIRED","SESSION_REQUIRED","ACCOUNT_DISABLED","EMAIL_NOT_CONFIGURED","EMAIL_FAILED","UPSTREAM_ERROR","UNAVAILABLE"requestIdstringrequiredQuote this when reporting a problem
detailsarray of objectValidation errors only (400 INVALID_REQUEST) - every problem found, with its field path
fieldstringmessagestring
400INVALID_REQUEST: invalid body or parameter (the message names the field)
{
"message": "Daily limit reached (50 videos per 24 hours); try again later",
"error": "DAILY_LIMIT",
"requestId": "string",
"details": [
{
"field": "edit.clips[0].end",
"message": "80s is past the end of video …, which is 60.0s (1:00); use null for \"to the end\""
}
]
}Schema
messagestringrequiredHuman-readable; wording may change
errorstringrequiredStable machine-readable code (new codes may be added; fall back to the HTTP status)
one of"INVALID_REQUEST","UNAUTHORIZED","MISSING_AUTHENTICATION","FORBIDDEN","REQUEST_REJECTED","NOT_FOUND","ROUTE_NOT_FOUND","METHOD_NOT_ALLOWED","UPLOAD_INCOMPLETE","ALREADY_COMPLETED","ORIGINAL_UNAVAILABLE","EDIT_SOURCE_NOT_READY","NOT_AN_UPLOAD","IDEMPOTENCY_KEY_REUSED","IDEMPOTENCY_IN_PROGRESS","INVALID_STATE","TOO_LARGE","RATE_LIMITED","QUOTA_EXCEEDED","DAILY_LIMIT","VIDEOS_PAUSED","PLAN_UPGRADE_REQUIRED","PLAN_LIMIT_REACHED","INVALID_CODE","TERMS_REQUIRED","SESSION_REQUIRED","ACCOUNT_DISABLED","EMAIL_NOT_CONFIGURED","EMAIL_FAILED","UPSTREAM_ERROR","UNAVAILABLE"requestIdstringrequiredQuote this when reporting a problem
detailsarray of objectValidation errors only (400 INVALID_REQUEST) - every problem found, with its field path
fieldstringmessagestring
401UNAUTHORIZED: missing, unknown or revoked API key
{
"message": "Daily limit reached (50 videos per 24 hours); try again later",
"error": "DAILY_LIMIT",
"requestId": "string",
"details": [
{
"field": "edit.clips[0].end",
"message": "80s is past the end of video …, which is 60.0s (1:00); use null for \"to the end\""
}
]
}Schema
messagestringrequiredHuman-readable; wording may change
errorstringrequiredStable machine-readable code (new codes may be added; fall back to the HTTP status)
one of"INVALID_REQUEST","UNAUTHORIZED","MISSING_AUTHENTICATION","FORBIDDEN","REQUEST_REJECTED","NOT_FOUND","ROUTE_NOT_FOUND","METHOD_NOT_ALLOWED","UPLOAD_INCOMPLETE","ALREADY_COMPLETED","ORIGINAL_UNAVAILABLE","EDIT_SOURCE_NOT_READY","NOT_AN_UPLOAD","IDEMPOTENCY_KEY_REUSED","IDEMPOTENCY_IN_PROGRESS","INVALID_STATE","TOO_LARGE","RATE_LIMITED","QUOTA_EXCEEDED","DAILY_LIMIT","VIDEOS_PAUSED","PLAN_UPGRADE_REQUIRED","PLAN_LIMIT_REACHED","INVALID_CODE","TERMS_REQUIRED","SESSION_REQUIRED","ACCOUNT_DISABLED","EMAIL_NOT_CONFIGURED","EMAIL_FAILED","UPSTREAM_ERROR","UNAVAILABLE"requestIdstringrequiredQuote this when reporting a problem
detailsarray of objectValidation errors only (400 INVALID_REQUEST) - every problem found, with its field path
fieldstringmessagestring
404NOT_FOUND: no such video for this user (wrong ID or someone else's)
{
"message": "Daily limit reached (50 videos per 24 hours); try again later",
"error": "DAILY_LIMIT",
"requestId": "string",
"details": [
{
"field": "edit.clips[0].end",
"message": "80s is past the end of video …, which is 60.0s (1:00); use null for \"to the end\""
}
]
}Schema
messagestringrequiredHuman-readable; wording may change
errorstringrequiredStable machine-readable code (new codes may be added; fall back to the HTTP status)
one of"INVALID_REQUEST","UNAUTHORIZED","MISSING_AUTHENTICATION","FORBIDDEN","REQUEST_REJECTED","NOT_FOUND","ROUTE_NOT_FOUND","METHOD_NOT_ALLOWED","UPLOAD_INCOMPLETE","ALREADY_COMPLETED","ORIGINAL_UNAVAILABLE","EDIT_SOURCE_NOT_READY","NOT_AN_UPLOAD","IDEMPOTENCY_KEY_REUSED","IDEMPOTENCY_IN_PROGRESS","INVALID_STATE","TOO_LARGE","RATE_LIMITED","QUOTA_EXCEEDED","DAILY_LIMIT","VIDEOS_PAUSED","PLAN_UPGRADE_REQUIRED","PLAN_LIMIT_REACHED","INVALID_CODE","TERMS_REQUIRED","SESSION_REQUIRED","ACCOUNT_DISABLED","EMAIL_NOT_CONFIGURED","EMAIL_FAILED","UPSTREAM_ERROR","UNAVAILABLE"requestIdstringrequiredQuote this when reporting a problem
detailsarray of objectValidation errors only (400 INVALID_REQUEST) - every problem found, with its field path
fieldstringmessagestring
429RATE_LIMITED (retry with backoff), QUOTA_EXCEEDED, DAILY_LIMIT or VIDEOS_PAUSED
{
"message": "Daily limit reached (50 videos per 24 hours); try again later",
"error": "DAILY_LIMIT",
"requestId": "string",
"details": [
{
"field": "edit.clips[0].end",
"message": "80s is past the end of video …, which is 60.0s (1:00); use null for \"to the end\""
}
]
}Schema
messagestringrequiredHuman-readable; wording may change
errorstringrequiredStable machine-readable code (new codes may be added; fall back to the HTTP status)
one of"INVALID_REQUEST","UNAUTHORIZED","MISSING_AUTHENTICATION","FORBIDDEN","REQUEST_REJECTED","NOT_FOUND","ROUTE_NOT_FOUND","METHOD_NOT_ALLOWED","UPLOAD_INCOMPLETE","ALREADY_COMPLETED","ORIGINAL_UNAVAILABLE","EDIT_SOURCE_NOT_READY","NOT_AN_UPLOAD","IDEMPOTENCY_KEY_REUSED","IDEMPOTENCY_IN_PROGRESS","INVALID_STATE","TOO_LARGE","RATE_LIMITED","QUOTA_EXCEEDED","DAILY_LIMIT","VIDEOS_PAUSED","PLAN_UPGRADE_REQUIRED","PLAN_LIMIT_REACHED","INVALID_CODE","TERMS_REQUIRED","SESSION_REQUIRED","ACCOUNT_DISABLED","EMAIL_NOT_CONFIGURED","EMAIL_FAILED","UPSTREAM_ERROR","UNAVAILABLE"requestIdstringrequiredQuote this when reporting a problem
detailsarray of objectValidation errors only (400 INVALID_REQUEST) - every problem found, with its field path
fieldstringmessagestring
503UNAVAILABLE: temporary failure; retry with backoff
{
"message": "Daily limit reached (50 videos per 24 hours); try again later",
"error": "DAILY_LIMIT",
"requestId": "string",
"details": [
{
"field": "edit.clips[0].end",
"message": "80s is past the end of video …, which is 60.0s (1:00); use null for \"to the end\""
}
]
}Schema
messagestringrequiredHuman-readable; wording may change
errorstringrequiredStable machine-readable code (new codes may be added; fall back to the HTTP status)
one of"INVALID_REQUEST","UNAUTHORIZED","MISSING_AUTHENTICATION","FORBIDDEN","REQUEST_REJECTED","NOT_FOUND","ROUTE_NOT_FOUND","METHOD_NOT_ALLOWED","UPLOAD_INCOMPLETE","ALREADY_COMPLETED","ORIGINAL_UNAVAILABLE","EDIT_SOURCE_NOT_READY","NOT_AN_UPLOAD","IDEMPOTENCY_KEY_REUSED","IDEMPOTENCY_IN_PROGRESS","INVALID_STATE","TOO_LARGE","RATE_LIMITED","QUOTA_EXCEEDED","DAILY_LIMIT","VIDEOS_PAUSED","PLAN_UPGRADE_REQUIRED","PLAN_LIMIT_REACHED","INVALID_CODE","TERMS_REQUIRED","SESSION_REQUIRED","ACCOUNT_DISABLED","EMAIL_NOT_CONFIGURED","EMAIL_FAILED","UPSTREAM_ERROR","UNAVAILABLE"requestIdstringrequiredQuote this when reporting a problem
detailsarray of objectValidation errors only (400 INVALID_REQUEST) - every problem found, with its field path
fieldstringmessagestring
operationId abortUpload
POST/videos/{videoId}/heartbeatKeep a pre-started recording job waiting (send every ~10 s while recording)
If heartbeats stop for 2 minutes the waiting job gives up. To cancel on purpose, call abort.
Parameters
| Name | In | Type | Description |
|---|---|---|---|
videoIdrequired | path | string |
Responses
200OK
409Not an unfinished streaming upload with prestart
{
"message": "Daily limit reached (50 videos per 24 hours); try again later",
"error": "DAILY_LIMIT",
"requestId": "string",
"details": [
{
"field": "edit.clips[0].end",
"message": "80s is past the end of video …, which is 60.0s (1:00); use null for \"to the end\""
}
]
}Schema
messagestringrequiredHuman-readable; wording may change
errorstringrequiredStable machine-readable code (new codes may be added; fall back to the HTTP status)
one of"INVALID_REQUEST","UNAUTHORIZED","MISSING_AUTHENTICATION","FORBIDDEN","REQUEST_REJECTED","NOT_FOUND","ROUTE_NOT_FOUND","METHOD_NOT_ALLOWED","UPLOAD_INCOMPLETE","ALREADY_COMPLETED","ORIGINAL_UNAVAILABLE","EDIT_SOURCE_NOT_READY","NOT_AN_UPLOAD","IDEMPOTENCY_KEY_REUSED","IDEMPOTENCY_IN_PROGRESS","INVALID_STATE","TOO_LARGE","RATE_LIMITED","QUOTA_EXCEEDED","DAILY_LIMIT","VIDEOS_PAUSED","PLAN_UPGRADE_REQUIRED","PLAN_LIMIT_REACHED","INVALID_CODE","TERMS_REQUIRED","SESSION_REQUIRED","ACCOUNT_DISABLED","EMAIL_NOT_CONFIGURED","EMAIL_FAILED","UPSTREAM_ERROR","UNAVAILABLE"requestIdstringrequiredQuote this when reporting a problem
detailsarray of objectValidation errors only (400 INVALID_REQUEST) - every problem found, with its field path
fieldstringmessagestring
400INVALID_REQUEST: invalid body or parameter (the message names the field)
{
"message": "Daily limit reached (50 videos per 24 hours); try again later",
"error": "DAILY_LIMIT",
"requestId": "string",
"details": [
{
"field": "edit.clips[0].end",
"message": "80s is past the end of video …, which is 60.0s (1:00); use null for \"to the end\""
}
]
}Schema
messagestringrequiredHuman-readable; wording may change
errorstringrequiredStable machine-readable code (new codes may be added; fall back to the HTTP status)
one of"INVALID_REQUEST","UNAUTHORIZED","MISSING_AUTHENTICATION","FORBIDDEN","REQUEST_REJECTED","NOT_FOUND","ROUTE_NOT_FOUND","METHOD_NOT_ALLOWED","UPLOAD_INCOMPLETE","ALREADY_COMPLETED","ORIGINAL_UNAVAILABLE","EDIT_SOURCE_NOT_READY","NOT_AN_UPLOAD","IDEMPOTENCY_KEY_REUSED","IDEMPOTENCY_IN_PROGRESS","INVALID_STATE","TOO_LARGE","RATE_LIMITED","QUOTA_EXCEEDED","DAILY_LIMIT","VIDEOS_PAUSED","PLAN_UPGRADE_REQUIRED","PLAN_LIMIT_REACHED","INVALID_CODE","TERMS_REQUIRED","SESSION_REQUIRED","ACCOUNT_DISABLED","EMAIL_NOT_CONFIGURED","EMAIL_FAILED","UPSTREAM_ERROR","UNAVAILABLE"requestIdstringrequiredQuote this when reporting a problem
detailsarray of objectValidation errors only (400 INVALID_REQUEST) - every problem found, with its field path
fieldstringmessagestring
401UNAUTHORIZED: missing, unknown or revoked API key
{
"message": "Daily limit reached (50 videos per 24 hours); try again later",
"error": "DAILY_LIMIT",
"requestId": "string",
"details": [
{
"field": "edit.clips[0].end",
"message": "80s is past the end of video …, which is 60.0s (1:00); use null for \"to the end\""
}
]
}Schema
messagestringrequiredHuman-readable; wording may change
errorstringrequiredStable machine-readable code (new codes may be added; fall back to the HTTP status)
one of"INVALID_REQUEST","UNAUTHORIZED","MISSING_AUTHENTICATION","FORBIDDEN","REQUEST_REJECTED","NOT_FOUND","ROUTE_NOT_FOUND","METHOD_NOT_ALLOWED","UPLOAD_INCOMPLETE","ALREADY_COMPLETED","ORIGINAL_UNAVAILABLE","EDIT_SOURCE_NOT_READY","NOT_AN_UPLOAD","IDEMPOTENCY_KEY_REUSED","IDEMPOTENCY_IN_PROGRESS","INVALID_STATE","TOO_LARGE","RATE_LIMITED","QUOTA_EXCEEDED","DAILY_LIMIT","VIDEOS_PAUSED","PLAN_UPGRADE_REQUIRED","PLAN_LIMIT_REACHED","INVALID_CODE","TERMS_REQUIRED","SESSION_REQUIRED","ACCOUNT_DISABLED","EMAIL_NOT_CONFIGURED","EMAIL_FAILED","UPSTREAM_ERROR","UNAVAILABLE"requestIdstringrequiredQuote this when reporting a problem
detailsarray of objectValidation errors only (400 INVALID_REQUEST) - every problem found, with its field path
fieldstringmessagestring
404NOT_FOUND: no such video for this user (wrong ID or someone else's)
{
"message": "Daily limit reached (50 videos per 24 hours); try again later",
"error": "DAILY_LIMIT",
"requestId": "string",
"details": [
{
"field": "edit.clips[0].end",
"message": "80s is past the end of video …, which is 60.0s (1:00); use null for \"to the end\""
}
]
}Schema
messagestringrequiredHuman-readable; wording may change
errorstringrequiredStable machine-readable code (new codes may be added; fall back to the HTTP status)
one of"INVALID_REQUEST","UNAUTHORIZED","MISSING_AUTHENTICATION","FORBIDDEN","REQUEST_REJECTED","NOT_FOUND","ROUTE_NOT_FOUND","METHOD_NOT_ALLOWED","UPLOAD_INCOMPLETE","ALREADY_COMPLETED","ORIGINAL_UNAVAILABLE","EDIT_SOURCE_NOT_READY","NOT_AN_UPLOAD","IDEMPOTENCY_KEY_REUSED","IDEMPOTENCY_IN_PROGRESS","INVALID_STATE","TOO_LARGE","RATE_LIMITED","QUOTA_EXCEEDED","DAILY_LIMIT","VIDEOS_PAUSED","PLAN_UPGRADE_REQUIRED","PLAN_LIMIT_REACHED","INVALID_CODE","TERMS_REQUIRED","SESSION_REQUIRED","ACCOUNT_DISABLED","EMAIL_NOT_CONFIGURED","EMAIL_FAILED","UPSTREAM_ERROR","UNAVAILABLE"requestIdstringrequiredQuote this when reporting a problem
detailsarray of objectValidation errors only (400 INVALID_REQUEST) - every problem found, with its field path
fieldstringmessagestring
429RATE_LIMITED (retry with backoff), QUOTA_EXCEEDED, DAILY_LIMIT or VIDEOS_PAUSED
{
"message": "Daily limit reached (50 videos per 24 hours); try again later",
"error": "DAILY_LIMIT",
"requestId": "string",
"details": [
{
"field": "edit.clips[0].end",
"message": "80s is past the end of video …, which is 60.0s (1:00); use null for \"to the end\""
}
]
}Schema
messagestringrequiredHuman-readable; wording may change
errorstringrequiredStable machine-readable code (new codes may be added; fall back to the HTTP status)
one of"INVALID_REQUEST","UNAUTHORIZED","MISSING_AUTHENTICATION","FORBIDDEN","REQUEST_REJECTED","NOT_FOUND","ROUTE_NOT_FOUND","METHOD_NOT_ALLOWED","UPLOAD_INCOMPLETE","ALREADY_COMPLETED","ORIGINAL_UNAVAILABLE","EDIT_SOURCE_NOT_READY","NOT_AN_UPLOAD","IDEMPOTENCY_KEY_REUSED","IDEMPOTENCY_IN_PROGRESS","INVALID_STATE","TOO_LARGE","RATE_LIMITED","QUOTA_EXCEEDED","DAILY_LIMIT","VIDEOS_PAUSED","PLAN_UPGRADE_REQUIRED","PLAN_LIMIT_REACHED","INVALID_CODE","TERMS_REQUIRED","SESSION_REQUIRED","ACCOUNT_DISABLED","EMAIL_NOT_CONFIGURED","EMAIL_FAILED","UPSTREAM_ERROR","UNAVAILABLE"requestIdstringrequiredQuote this when reporting a problem
detailsarray of objectValidation errors only (400 INVALID_REQUEST) - every problem found, with its field path
fieldstringmessagestring
503UNAVAILABLE: temporary failure; retry with backoff
{
"message": "Daily limit reached (50 videos per 24 hours); try again later",
"error": "DAILY_LIMIT",
"requestId": "string",
"details": [
{
"field": "edit.clips[0].end",
"message": "80s is past the end of video …, which is 60.0s (1:00); use null for \"to the end\""
}
]
}Schema
messagestringrequiredHuman-readable; wording may change
errorstringrequiredStable machine-readable code (new codes may be added; fall back to the HTTP status)
one of"INVALID_REQUEST","UNAUTHORIZED","MISSING_AUTHENTICATION","FORBIDDEN","REQUEST_REJECTED","NOT_FOUND","ROUTE_NOT_FOUND","METHOD_NOT_ALLOWED","UPLOAD_INCOMPLETE","ALREADY_COMPLETED","ORIGINAL_UNAVAILABLE","EDIT_SOURCE_NOT_READY","NOT_AN_UPLOAD","IDEMPOTENCY_KEY_REUSED","IDEMPOTENCY_IN_PROGRESS","INVALID_STATE","TOO_LARGE","RATE_LIMITED","QUOTA_EXCEEDED","DAILY_LIMIT","VIDEOS_PAUSED","PLAN_UPGRADE_REQUIRED","PLAN_LIMIT_REACHED","INVALID_CODE","TERMS_REQUIRED","SESSION_REQUIRED","ACCOUNT_DISABLED","EMAIL_NOT_CONFIGURED","EMAIL_FAILED","UPSTREAM_ERROR","UNAVAILABLE"requestIdstringrequiredQuote this when reporting a problem
detailsarray of objectValidation errors only (400 INVALID_REQUEST) - every problem found, with its field path
fieldstringmessagestring
operationId sendHeartbeat
Processing
Stop processing, or process a finished video again with new options.
POST/videos/{videoId}/cancelStop processing (the workflow and any of its queued or running jobs)
Parameters
| Name | In | Type | Description |
|---|---|---|---|
videoIdrequired | path | string |
Responses
200Stopped; includes terminatedJobs
400INVALID_REQUEST: invalid body or parameter (the message names the field)
{
"message": "Daily limit reached (50 videos per 24 hours); try again later",
"error": "DAILY_LIMIT",
"requestId": "string",
"details": [
{
"field": "edit.clips[0].end",
"message": "80s is past the end of video …, which is 60.0s (1:00); use null for \"to the end\""
}
]
}Schema
messagestringrequiredHuman-readable; wording may change
errorstringrequiredStable machine-readable code (new codes may be added; fall back to the HTTP status)
one of"INVALID_REQUEST","UNAUTHORIZED","MISSING_AUTHENTICATION","FORBIDDEN","REQUEST_REJECTED","NOT_FOUND","ROUTE_NOT_FOUND","METHOD_NOT_ALLOWED","UPLOAD_INCOMPLETE","ALREADY_COMPLETED","ORIGINAL_UNAVAILABLE","EDIT_SOURCE_NOT_READY","NOT_AN_UPLOAD","IDEMPOTENCY_KEY_REUSED","IDEMPOTENCY_IN_PROGRESS","INVALID_STATE","TOO_LARGE","RATE_LIMITED","QUOTA_EXCEEDED","DAILY_LIMIT","VIDEOS_PAUSED","PLAN_UPGRADE_REQUIRED","PLAN_LIMIT_REACHED","INVALID_CODE","TERMS_REQUIRED","SESSION_REQUIRED","ACCOUNT_DISABLED","EMAIL_NOT_CONFIGURED","EMAIL_FAILED","UPSTREAM_ERROR","UNAVAILABLE"requestIdstringrequiredQuote this when reporting a problem
detailsarray of objectValidation errors only (400 INVALID_REQUEST) - every problem found, with its field path
fieldstringmessagestring
401UNAUTHORIZED: missing, unknown or revoked API key
{
"message": "Daily limit reached (50 videos per 24 hours); try again later",
"error": "DAILY_LIMIT",
"requestId": "string",
"details": [
{
"field": "edit.clips[0].end",
"message": "80s is past the end of video …, which is 60.0s (1:00); use null for \"to the end\""
}
]
}Schema
messagestringrequiredHuman-readable; wording may change
errorstringrequiredStable machine-readable code (new codes may be added; fall back to the HTTP status)
one of"INVALID_REQUEST","UNAUTHORIZED","MISSING_AUTHENTICATION","FORBIDDEN","REQUEST_REJECTED","NOT_FOUND","ROUTE_NOT_FOUND","METHOD_NOT_ALLOWED","UPLOAD_INCOMPLETE","ALREADY_COMPLETED","ORIGINAL_UNAVAILABLE","EDIT_SOURCE_NOT_READY","NOT_AN_UPLOAD","IDEMPOTENCY_KEY_REUSED","IDEMPOTENCY_IN_PROGRESS","INVALID_STATE","TOO_LARGE","RATE_LIMITED","QUOTA_EXCEEDED","DAILY_LIMIT","VIDEOS_PAUSED","PLAN_UPGRADE_REQUIRED","PLAN_LIMIT_REACHED","INVALID_CODE","TERMS_REQUIRED","SESSION_REQUIRED","ACCOUNT_DISABLED","EMAIL_NOT_CONFIGURED","EMAIL_FAILED","UPSTREAM_ERROR","UNAVAILABLE"requestIdstringrequiredQuote this when reporting a problem
detailsarray of objectValidation errors only (400 INVALID_REQUEST) - every problem found, with its field path
fieldstringmessagestring
404NOT_FOUND: no such video for this user (wrong ID or someone else's)
{
"message": "Daily limit reached (50 videos per 24 hours); try again later",
"error": "DAILY_LIMIT",
"requestId": "string",
"details": [
{
"field": "edit.clips[0].end",
"message": "80s is past the end of video …, which is 60.0s (1:00); use null for \"to the end\""
}
]
}Schema
messagestringrequiredHuman-readable; wording may change
errorstringrequiredStable machine-readable code (new codes may be added; fall back to the HTTP status)
one of"INVALID_REQUEST","UNAUTHORIZED","MISSING_AUTHENTICATION","FORBIDDEN","REQUEST_REJECTED","NOT_FOUND","ROUTE_NOT_FOUND","METHOD_NOT_ALLOWED","UPLOAD_INCOMPLETE","ALREADY_COMPLETED","ORIGINAL_UNAVAILABLE","EDIT_SOURCE_NOT_READY","NOT_AN_UPLOAD","IDEMPOTENCY_KEY_REUSED","IDEMPOTENCY_IN_PROGRESS","INVALID_STATE","TOO_LARGE","RATE_LIMITED","QUOTA_EXCEEDED","DAILY_LIMIT","VIDEOS_PAUSED","PLAN_UPGRADE_REQUIRED","PLAN_LIMIT_REACHED","INVALID_CODE","TERMS_REQUIRED","SESSION_REQUIRED","ACCOUNT_DISABLED","EMAIL_NOT_CONFIGURED","EMAIL_FAILED","UPSTREAM_ERROR","UNAVAILABLE"requestIdstringrequiredQuote this when reporting a problem
detailsarray of objectValidation errors only (400 INVALID_REQUEST) - every problem found, with its field path
fieldstringmessagestring
429RATE_LIMITED (retry with backoff), QUOTA_EXCEEDED, DAILY_LIMIT or VIDEOS_PAUSED
{
"message": "Daily limit reached (50 videos per 24 hours); try again later",
"error": "DAILY_LIMIT",
"requestId": "string",
"details": [
{
"field": "edit.clips[0].end",
"message": "80s is past the end of video …, which is 60.0s (1:00); use null for \"to the end\""
}
]
}Schema
messagestringrequiredHuman-readable; wording may change
errorstringrequiredStable machine-readable code (new codes may be added; fall back to the HTTP status)
one of"INVALID_REQUEST","UNAUTHORIZED","MISSING_AUTHENTICATION","FORBIDDEN","REQUEST_REJECTED","NOT_FOUND","ROUTE_NOT_FOUND","METHOD_NOT_ALLOWED","UPLOAD_INCOMPLETE","ALREADY_COMPLETED","ORIGINAL_UNAVAILABLE","EDIT_SOURCE_NOT_READY","NOT_AN_UPLOAD","IDEMPOTENCY_KEY_REUSED","IDEMPOTENCY_IN_PROGRESS","INVALID_STATE","TOO_LARGE","RATE_LIMITED","QUOTA_EXCEEDED","DAILY_LIMIT","VIDEOS_PAUSED","PLAN_UPGRADE_REQUIRED","PLAN_LIMIT_REACHED","INVALID_CODE","TERMS_REQUIRED","SESSION_REQUIRED","ACCOUNT_DISABLED","EMAIL_NOT_CONFIGURED","EMAIL_FAILED","UPSTREAM_ERROR","UNAVAILABLE"requestIdstringrequiredQuote this when reporting a problem
detailsarray of objectValidation errors only (400 INVALID_REQUEST) - every problem found, with its field path
fieldstringmessagestring
503UNAVAILABLE: temporary failure; retry with backoff
{
"message": "Daily limit reached (50 videos per 24 hours); try again later",
"error": "DAILY_LIMIT",
"requestId": "string",
"details": [
{
"field": "edit.clips[0].end",
"message": "80s is past the end of video …, which is 60.0s (1:00); use null for \"to the end\""
}
]
}Schema
messagestringrequiredHuman-readable; wording may change
errorstringrequiredStable machine-readable code (new codes may be added; fall back to the HTTP status)
one of"INVALID_REQUEST","UNAUTHORIZED","MISSING_AUTHENTICATION","FORBIDDEN","REQUEST_REJECTED","NOT_FOUND","ROUTE_NOT_FOUND","METHOD_NOT_ALLOWED","UPLOAD_INCOMPLETE","ALREADY_COMPLETED","ORIGINAL_UNAVAILABLE","EDIT_SOURCE_NOT_READY","NOT_AN_UPLOAD","IDEMPOTENCY_KEY_REUSED","IDEMPOTENCY_IN_PROGRESS","INVALID_STATE","TOO_LARGE","RATE_LIMITED","QUOTA_EXCEEDED","DAILY_LIMIT","VIDEOS_PAUSED","PLAN_UPGRADE_REQUIRED","PLAN_LIMIT_REACHED","INVALID_CODE","TERMS_REQUIRED","SESSION_REQUIRED","ACCOUNT_DISABLED","EMAIL_NOT_CONFIGURED","EMAIL_FAILED","UPSTREAM_ERROR","UNAVAILABLE"requestIdstringrequiredQuote this when reporting a problem
detailsarray of objectValidation errors only (400 INVALID_REQUEST) - every problem found, with its field path
fieldstringmessagestring
operationId cancelProcessing
POST/videos/{videoId}/reprocessProcess a video again as a new video (new quality, trim and/or redactions)
Reuses the original upload (nothing is re-sent); for an edit, renders the same clips again. Options are not inherited. Not available after deleteOriginal.
Parameters
| Name | In | Type | Description |
|---|---|---|---|
dryRun | query | boolean | Validate only: nothing is created or started (answers 200 with valid: true) |
videoIdrequired | path | string | |
Idempotency-Key | header | string | Unique key for idempotent requests: POST /videos, POST /videos/{id}/complete, POST /videos/{id}/reprocess. Same key with same request = replay of first response. Stored 24 hours. |
Request body
{
"maxHeight": 720,
"redactions": [
{
"type": "blur",
"shape": "rect",
"x": 0,
"y": 0,
"w": 0,
"h": 0,
"start": 0,
"end": 0,
"track": [
{
"time": 0,
"x": 0,
"y": 0,
"w": 0,
"h": 0
}
]
}
],
"deleteOriginal": false,
"trim": {
"start": 0,
"end": 0
},
"metadata": {}
}Schema
maxHeightintegerone of720,1080,1440,2160redactionsarray of Redactionmax items20typestringrequiredbox = solid black bar
one of"blur","box"shapestringone of"rect","ellipse"· default"rect"xnumberFraction of the final frame width
min0· max1ynumbermin0· max1wnumbermin0· max1hnumbermin0· max1startnumberSeconds
default0endnumberSeconds; null = until the end
trackarray of objectKeyframes for moving content, in time order; the region glides between them (instead of x, y, w, h)
max items50timenumberrequiredSeconds on the output timeline
min0xnumberrequiredmin0· max1ynumberrequiredmin0· max1wnumberrequiredmin0· max1hnumberrequiredmin0· max1
deleteOriginalbooleanUploads only
trimTrimKeep only part of the upload (frame-accurate, no extra encode). Redaction times are on the trimmed timeline.
startnumbermin0· default0endnumbernull = to the end
metadataMetadataCustomer key-value pairs (up to 20 keys, 4 KB total). Keys are 1-40 [A-Za-z0-9_.-]; values are strings up to 500 chars.
Responses
201New video created; poll its status
{
"videoId": "string",
"reprocessOf": "string"
}Schema
videoIdstringreprocessOfstring
400INVALID_REQUEST: invalid body or parameter (the message names the field)
{
"message": "Daily limit reached (50 videos per 24 hours); try again later",
"error": "DAILY_LIMIT",
"requestId": "string",
"details": [
{
"field": "edit.clips[0].end",
"message": "80s is past the end of video …, which is 60.0s (1:00); use null for \"to the end\""
}
]
}Schema
messagestringrequiredHuman-readable; wording may change
errorstringrequiredStable machine-readable code (new codes may be added; fall back to the HTTP status)
one of"INVALID_REQUEST","UNAUTHORIZED","MISSING_AUTHENTICATION","FORBIDDEN","REQUEST_REJECTED","NOT_FOUND","ROUTE_NOT_FOUND","METHOD_NOT_ALLOWED","UPLOAD_INCOMPLETE","ALREADY_COMPLETED","ORIGINAL_UNAVAILABLE","EDIT_SOURCE_NOT_READY","NOT_AN_UPLOAD","IDEMPOTENCY_KEY_REUSED","IDEMPOTENCY_IN_PROGRESS","INVALID_STATE","TOO_LARGE","RATE_LIMITED","QUOTA_EXCEEDED","DAILY_LIMIT","VIDEOS_PAUSED","PLAN_UPGRADE_REQUIRED","PLAN_LIMIT_REACHED","INVALID_CODE","TERMS_REQUIRED","SESSION_REQUIRED","ACCOUNT_DISABLED","EMAIL_NOT_CONFIGURED","EMAIL_FAILED","UPSTREAM_ERROR","UNAVAILABLE"requestIdstringrequiredQuote this when reporting a problem
detailsarray of objectValidation errors only (400 INVALID_REQUEST) - every problem found, with its field path
fieldstringmessagestring
401UNAUTHORIZED: missing, unknown or revoked API key
{
"message": "Daily limit reached (50 videos per 24 hours); try again later",
"error": "DAILY_LIMIT",
"requestId": "string",
"details": [
{
"field": "edit.clips[0].end",
"message": "80s is past the end of video …, which is 60.0s (1:00); use null for \"to the end\""
}
]
}Schema
messagestringrequiredHuman-readable; wording may change
errorstringrequiredStable machine-readable code (new codes may be added; fall back to the HTTP status)
one of"INVALID_REQUEST","UNAUTHORIZED","MISSING_AUTHENTICATION","FORBIDDEN","REQUEST_REJECTED","NOT_FOUND","ROUTE_NOT_FOUND","METHOD_NOT_ALLOWED","UPLOAD_INCOMPLETE","ALREADY_COMPLETED","ORIGINAL_UNAVAILABLE","EDIT_SOURCE_NOT_READY","NOT_AN_UPLOAD","IDEMPOTENCY_KEY_REUSED","IDEMPOTENCY_IN_PROGRESS","INVALID_STATE","TOO_LARGE","RATE_LIMITED","QUOTA_EXCEEDED","DAILY_LIMIT","VIDEOS_PAUSED","PLAN_UPGRADE_REQUIRED","PLAN_LIMIT_REACHED","INVALID_CODE","TERMS_REQUIRED","SESSION_REQUIRED","ACCOUNT_DISABLED","EMAIL_NOT_CONFIGURED","EMAIL_FAILED","UPSTREAM_ERROR","UNAVAILABLE"requestIdstringrequiredQuote this when reporting a problem
detailsarray of objectValidation errors only (400 INVALID_REQUEST) - every problem found, with its field path
fieldstringmessagestring
403Plan does not allow the requested quality or feature, or plan limit reached (free tier only); reprocessing at a lower quality if the plan allows
{
"message": "Daily limit reached (50 videos per 24 hours); try again later",
"error": "DAILY_LIMIT",
"requestId": "string",
"details": [
{
"field": "edit.clips[0].end",
"message": "80s is past the end of video …, which is 60.0s (1:00); use null for \"to the end\""
}
]
}Schema
messagestringrequiredHuman-readable; wording may change
errorstringrequiredStable machine-readable code (new codes may be added; fall back to the HTTP status)
one of"INVALID_REQUEST","UNAUTHORIZED","MISSING_AUTHENTICATION","FORBIDDEN","REQUEST_REJECTED","NOT_FOUND","ROUTE_NOT_FOUND","METHOD_NOT_ALLOWED","UPLOAD_INCOMPLETE","ALREADY_COMPLETED","ORIGINAL_UNAVAILABLE","EDIT_SOURCE_NOT_READY","NOT_AN_UPLOAD","IDEMPOTENCY_KEY_REUSED","IDEMPOTENCY_IN_PROGRESS","INVALID_STATE","TOO_LARGE","RATE_LIMITED","QUOTA_EXCEEDED","DAILY_LIMIT","VIDEOS_PAUSED","PLAN_UPGRADE_REQUIRED","PLAN_LIMIT_REACHED","INVALID_CODE","TERMS_REQUIRED","SESSION_REQUIRED","ACCOUNT_DISABLED","EMAIL_NOT_CONFIGURED","EMAIL_FAILED","UPSTREAM_ERROR","UNAVAILABLE"requestIdstringrequiredQuote this when reporting a problem
detailsarray of objectValidation errors only (400 INVALID_REQUEST) - every problem found, with its field path
fieldstringmessagestring
404NOT_FOUND: no such video for this user (wrong ID or someone else's)
{
"message": "Daily limit reached (50 videos per 24 hours); try again later",
"error": "DAILY_LIMIT",
"requestId": "string",
"details": [
{
"field": "edit.clips[0].end",
"message": "80s is past the end of video …, which is 60.0s (1:00); use null for \"to the end\""
}
]
}Schema
messagestringrequiredHuman-readable; wording may change
errorstringrequiredStable machine-readable code (new codes may be added; fall back to the HTTP status)
one of"INVALID_REQUEST","UNAUTHORIZED","MISSING_AUTHENTICATION","FORBIDDEN","REQUEST_REJECTED","NOT_FOUND","ROUTE_NOT_FOUND","METHOD_NOT_ALLOWED","UPLOAD_INCOMPLETE","ALREADY_COMPLETED","ORIGINAL_UNAVAILABLE","EDIT_SOURCE_NOT_READY","NOT_AN_UPLOAD","IDEMPOTENCY_KEY_REUSED","IDEMPOTENCY_IN_PROGRESS","INVALID_STATE","TOO_LARGE","RATE_LIMITED","QUOTA_EXCEEDED","DAILY_LIMIT","VIDEOS_PAUSED","PLAN_UPGRADE_REQUIRED","PLAN_LIMIT_REACHED","INVALID_CODE","TERMS_REQUIRED","SESSION_REQUIRED","ACCOUNT_DISABLED","EMAIL_NOT_CONFIGURED","EMAIL_FAILED","UPSTREAM_ERROR","UNAVAILABLE"requestIdstringrequiredQuote this when reporting a problem
detailsarray of objectValidation errors only (400 INVALID_REQUEST) - every problem found, with its field path
fieldstringmessagestring
409Original never completed or was deleted
{
"message": "Daily limit reached (50 videos per 24 hours); try again later",
"error": "DAILY_LIMIT",
"requestId": "string",
"details": [
{
"field": "edit.clips[0].end",
"message": "80s is past the end of video …, which is 60.0s (1:00); use null for \"to the end\""
}
]
}Schema
messagestringrequiredHuman-readable; wording may change
errorstringrequiredStable machine-readable code (new codes may be added; fall back to the HTTP status)
one of"INVALID_REQUEST","UNAUTHORIZED","MISSING_AUTHENTICATION","FORBIDDEN","REQUEST_REJECTED","NOT_FOUND","ROUTE_NOT_FOUND","METHOD_NOT_ALLOWED","UPLOAD_INCOMPLETE","ALREADY_COMPLETED","ORIGINAL_UNAVAILABLE","EDIT_SOURCE_NOT_READY","NOT_AN_UPLOAD","IDEMPOTENCY_KEY_REUSED","IDEMPOTENCY_IN_PROGRESS","INVALID_STATE","TOO_LARGE","RATE_LIMITED","QUOTA_EXCEEDED","DAILY_LIMIT","VIDEOS_PAUSED","PLAN_UPGRADE_REQUIRED","PLAN_LIMIT_REACHED","INVALID_CODE","TERMS_REQUIRED","SESSION_REQUIRED","ACCOUNT_DISABLED","EMAIL_NOT_CONFIGURED","EMAIL_FAILED","UPSTREAM_ERROR","UNAVAILABLE"requestIdstringrequiredQuote this when reporting a problem
detailsarray of objectValidation errors only (400 INVALID_REQUEST) - every problem found, with its field path
fieldstringmessagestring
429RATE_LIMITED (retry with backoff), QUOTA_EXCEEDED, DAILY_LIMIT or VIDEOS_PAUSED
{
"message": "Daily limit reached (50 videos per 24 hours); try again later",
"error": "DAILY_LIMIT",
"requestId": "string",
"details": [
{
"field": "edit.clips[0].end",
"message": "80s is past the end of video …, which is 60.0s (1:00); use null for \"to the end\""
}
]
}Schema
messagestringrequiredHuman-readable; wording may change
errorstringrequiredStable machine-readable code (new codes may be added; fall back to the HTTP status)
one of"INVALID_REQUEST","UNAUTHORIZED","MISSING_AUTHENTICATION","FORBIDDEN","REQUEST_REJECTED","NOT_FOUND","ROUTE_NOT_FOUND","METHOD_NOT_ALLOWED","UPLOAD_INCOMPLETE","ALREADY_COMPLETED","ORIGINAL_UNAVAILABLE","EDIT_SOURCE_NOT_READY","NOT_AN_UPLOAD","IDEMPOTENCY_KEY_REUSED","IDEMPOTENCY_IN_PROGRESS","INVALID_STATE","TOO_LARGE","RATE_LIMITED","QUOTA_EXCEEDED","DAILY_LIMIT","VIDEOS_PAUSED","PLAN_UPGRADE_REQUIRED","PLAN_LIMIT_REACHED","INVALID_CODE","TERMS_REQUIRED","SESSION_REQUIRED","ACCOUNT_DISABLED","EMAIL_NOT_CONFIGURED","EMAIL_FAILED","UPSTREAM_ERROR","UNAVAILABLE"requestIdstringrequiredQuote this when reporting a problem
detailsarray of objectValidation errors only (400 INVALID_REQUEST) - every problem found, with its field path
fieldstringmessagestring
503UNAVAILABLE: temporary failure; retry with backoff
{
"message": "Daily limit reached (50 videos per 24 hours); try again later",
"error": "DAILY_LIMIT",
"requestId": "string",
"details": [
{
"field": "edit.clips[0].end",
"message": "80s is past the end of video …, which is 60.0s (1:00); use null for \"to the end\""
}
]
}Schema
messagestringrequiredHuman-readable; wording may change
errorstringrequiredStable machine-readable code (new codes may be added; fall back to the HTTP status)
one of"INVALID_REQUEST","UNAUTHORIZED","MISSING_AUTHENTICATION","FORBIDDEN","REQUEST_REJECTED","NOT_FOUND","ROUTE_NOT_FOUND","METHOD_NOT_ALLOWED","UPLOAD_INCOMPLETE","ALREADY_COMPLETED","ORIGINAL_UNAVAILABLE","EDIT_SOURCE_NOT_READY","NOT_AN_UPLOAD","IDEMPOTENCY_KEY_REUSED","IDEMPOTENCY_IN_PROGRESS","INVALID_STATE","TOO_LARGE","RATE_LIMITED","QUOTA_EXCEEDED","DAILY_LIMIT","VIDEOS_PAUSED","PLAN_UPGRADE_REQUIRED","PLAN_LIMIT_REACHED","INVALID_CODE","TERMS_REQUIRED","SESSION_REQUIRED","ACCOUNT_DISABLED","EMAIL_NOT_CONFIGURED","EMAIL_FAILED","UPSTREAM_ERROR","UNAVAILABLE"requestIdstringrequiredQuote this when reporting a problem
detailsarray of objectValidation errors only (400 INVALID_REQUEST) - every problem found, with its field path
fieldstringmessagestring
operationId reprocessVideo
Usage
Processing minutes and storage for billing.
GET/usageUsage for a calendar month (for billing)
Returns processing time and storage for a month, against the plan. Month defaults to the current UTC month.
Parameters
| Name | In | Type | Description |
|---|---|---|---|
month | query | string | YYYY-MM (default: current month) |
Responses
200Usage summary
{
"month": "2026-10",
"complete": false,
"processing": {
"videos": 0,
"outputMinutes": 0,
"billableMinutes": 0
},
"storage": {
"gbMonths": 0,
"storedGb": 0,
"note": "string"
},
"delivery": {
"gb": null,
"note": "string"
}
}Schema
monthstringcompletebooleanWhether the month is complete (now >= month end)
processingobjectvideosintegerCount of videos that finished processing
outputMinutesnumberSum of output duration (after trim/edit)
billableMinutesnumbermax(60
storageobjectgbMonthsnumberByte-time (bytes × time) for bytes kept
storedGbnumberWhat is stored right now
notestring
deliveryobjectgbnullNot metered yet
notestring
400INVALID_REQUEST: invalid body or parameter (the message names the field)
{
"message": "Daily limit reached (50 videos per 24 hours); try again later",
"error": "DAILY_LIMIT",
"requestId": "string",
"details": [
{
"field": "edit.clips[0].end",
"message": "80s is past the end of video …, which is 60.0s (1:00); use null for \"to the end\""
}
]
}Schema
messagestringrequiredHuman-readable; wording may change
errorstringrequiredStable machine-readable code (new codes may be added; fall back to the HTTP status)
one of"INVALID_REQUEST","UNAUTHORIZED","MISSING_AUTHENTICATION","FORBIDDEN","REQUEST_REJECTED","NOT_FOUND","ROUTE_NOT_FOUND","METHOD_NOT_ALLOWED","UPLOAD_INCOMPLETE","ALREADY_COMPLETED","ORIGINAL_UNAVAILABLE","EDIT_SOURCE_NOT_READY","NOT_AN_UPLOAD","IDEMPOTENCY_KEY_REUSED","IDEMPOTENCY_IN_PROGRESS","INVALID_STATE","TOO_LARGE","RATE_LIMITED","QUOTA_EXCEEDED","DAILY_LIMIT","VIDEOS_PAUSED","PLAN_UPGRADE_REQUIRED","PLAN_LIMIT_REACHED","INVALID_CODE","TERMS_REQUIRED","SESSION_REQUIRED","ACCOUNT_DISABLED","EMAIL_NOT_CONFIGURED","EMAIL_FAILED","UPSTREAM_ERROR","UNAVAILABLE"requestIdstringrequiredQuote this when reporting a problem
detailsarray of objectValidation errors only (400 INVALID_REQUEST) - every problem found, with its field path
fieldstringmessagestring
401UNAUTHORIZED: missing, unknown or revoked API key
{
"message": "Daily limit reached (50 videos per 24 hours); try again later",
"error": "DAILY_LIMIT",
"requestId": "string",
"details": [
{
"field": "edit.clips[0].end",
"message": "80s is past the end of video …, which is 60.0s (1:00); use null for \"to the end\""
}
]
}Schema
messagestringrequiredHuman-readable; wording may change
errorstringrequiredStable machine-readable code (new codes may be added; fall back to the HTTP status)
one of"INVALID_REQUEST","UNAUTHORIZED","MISSING_AUTHENTICATION","FORBIDDEN","REQUEST_REJECTED","NOT_FOUND","ROUTE_NOT_FOUND","METHOD_NOT_ALLOWED","UPLOAD_INCOMPLETE","ALREADY_COMPLETED","ORIGINAL_UNAVAILABLE","EDIT_SOURCE_NOT_READY","NOT_AN_UPLOAD","IDEMPOTENCY_KEY_REUSED","IDEMPOTENCY_IN_PROGRESS","INVALID_STATE","TOO_LARGE","RATE_LIMITED","QUOTA_EXCEEDED","DAILY_LIMIT","VIDEOS_PAUSED","PLAN_UPGRADE_REQUIRED","PLAN_LIMIT_REACHED","INVALID_CODE","TERMS_REQUIRED","SESSION_REQUIRED","ACCOUNT_DISABLED","EMAIL_NOT_CONFIGURED","EMAIL_FAILED","UPSTREAM_ERROR","UNAVAILABLE"requestIdstringrequiredQuote this when reporting a problem
detailsarray of objectValidation errors only (400 INVALID_REQUEST) - every problem found, with its field path
fieldstringmessagestring
429RATE_LIMITED (retry with backoff), QUOTA_EXCEEDED, DAILY_LIMIT or VIDEOS_PAUSED
{
"message": "Daily limit reached (50 videos per 24 hours); try again later",
"error": "DAILY_LIMIT",
"requestId": "string",
"details": [
{
"field": "edit.clips[0].end",
"message": "80s is past the end of video …, which is 60.0s (1:00); use null for \"to the end\""
}
]
}Schema
messagestringrequiredHuman-readable; wording may change
errorstringrequiredStable machine-readable code (new codes may be added; fall back to the HTTP status)
one of"INVALID_REQUEST","UNAUTHORIZED","MISSING_AUTHENTICATION","FORBIDDEN","REQUEST_REJECTED","NOT_FOUND","ROUTE_NOT_FOUND","METHOD_NOT_ALLOWED","UPLOAD_INCOMPLETE","ALREADY_COMPLETED","ORIGINAL_UNAVAILABLE","EDIT_SOURCE_NOT_READY","NOT_AN_UPLOAD","IDEMPOTENCY_KEY_REUSED","IDEMPOTENCY_IN_PROGRESS","INVALID_STATE","TOO_LARGE","RATE_LIMITED","QUOTA_EXCEEDED","DAILY_LIMIT","VIDEOS_PAUSED","PLAN_UPGRADE_REQUIRED","PLAN_LIMIT_REACHED","INVALID_CODE","TERMS_REQUIRED","SESSION_REQUIRED","ACCOUNT_DISABLED","EMAIL_NOT_CONFIGURED","EMAIL_FAILED","UPSTREAM_ERROR","UNAVAILABLE"requestIdstringrequiredQuote this when reporting a problem
detailsarray of objectValidation errors only (400 INVALID_REQUEST) - every problem found, with its field path
fieldstringmessagestring
503UNAVAILABLE: temporary failure; retry with backoff
{
"message": "Daily limit reached (50 videos per 24 hours); try again later",
"error": "DAILY_LIMIT",
"requestId": "string",
"details": [
{
"field": "edit.clips[0].end",
"message": "80s is past the end of video …, which is 60.0s (1:00); use null for \"to the end\""
}
]
}Schema
messagestringrequiredHuman-readable; wording may change
errorstringrequiredStable machine-readable code (new codes may be added; fall back to the HTTP status)
one of"INVALID_REQUEST","UNAUTHORIZED","MISSING_AUTHENTICATION","FORBIDDEN","REQUEST_REJECTED","NOT_FOUND","ROUTE_NOT_FOUND","METHOD_NOT_ALLOWED","UPLOAD_INCOMPLETE","ALREADY_COMPLETED","ORIGINAL_UNAVAILABLE","EDIT_SOURCE_NOT_READY","NOT_AN_UPLOAD","IDEMPOTENCY_KEY_REUSED","IDEMPOTENCY_IN_PROGRESS","INVALID_STATE","TOO_LARGE","RATE_LIMITED","QUOTA_EXCEEDED","DAILY_LIMIT","VIDEOS_PAUSED","PLAN_UPGRADE_REQUIRED","PLAN_LIMIT_REACHED","INVALID_CODE","TERMS_REQUIRED","SESSION_REQUIRED","ACCOUNT_DISABLED","EMAIL_NOT_CONFIGURED","EMAIL_FAILED","UPSTREAM_ERROR","UNAVAILABLE"requestIdstringrequiredQuote this when reporting a problem
detailsarray of objectValidation errors only (400 INVALID_REQUEST) - every problem found, with its field path
fieldstringmessagestring
operationId getUsage
Authentication
Sign up and sign in with email, create sessions and API keys, manage your account.
POST/auth/startEmail a sign-in code (public; no auth required)
Sends a 6-digit code to the email address. 5 attempts per code, 10 minute expiry. Rate limited 5 per email per hour, 30 per IP per hour.
Request body required
{
"email": "user@example.com"
}Schema
emailstringrequired
Responses
200Code sent
{
"sent": false,
"expiresInSeconds": 0
}Schema
sentbooleanexpiresInSecondsinteger
400INVALID_REQUEST: invalid body or parameter (the message names the field)
{
"message": "Daily limit reached (50 videos per 24 hours); try again later",
"error": "DAILY_LIMIT",
"requestId": "string",
"details": [
{
"field": "edit.clips[0].end",
"message": "80s is past the end of video …, which is 60.0s (1:00); use null for \"to the end\""
}
]
}Schema
messagestringrequiredHuman-readable; wording may change
errorstringrequiredStable machine-readable code (new codes may be added; fall back to the HTTP status)
one of"INVALID_REQUEST","UNAUTHORIZED","MISSING_AUTHENTICATION","FORBIDDEN","REQUEST_REJECTED","NOT_FOUND","ROUTE_NOT_FOUND","METHOD_NOT_ALLOWED","UPLOAD_INCOMPLETE","ALREADY_COMPLETED","ORIGINAL_UNAVAILABLE","EDIT_SOURCE_NOT_READY","NOT_AN_UPLOAD","IDEMPOTENCY_KEY_REUSED","IDEMPOTENCY_IN_PROGRESS","INVALID_STATE","TOO_LARGE","RATE_LIMITED","QUOTA_EXCEEDED","DAILY_LIMIT","VIDEOS_PAUSED","PLAN_UPGRADE_REQUIRED","PLAN_LIMIT_REACHED","INVALID_CODE","TERMS_REQUIRED","SESSION_REQUIRED","ACCOUNT_DISABLED","EMAIL_NOT_CONFIGURED","EMAIL_FAILED","UPSTREAM_ERROR","UNAVAILABLE"requestIdstringrequiredQuote this when reporting a problem
detailsarray of objectValidation errors only (400 INVALID_REQUEST) - every problem found, with its field path
fieldstringmessagestring
429Rate limited
{
"message": "Daily limit reached (50 videos per 24 hours); try again later",
"error": "DAILY_LIMIT",
"requestId": "string",
"details": [
{
"field": "edit.clips[0].end",
"message": "80s is past the end of video …, which is 60.0s (1:00); use null for \"to the end\""
}
]
}Schema
messagestringrequiredHuman-readable; wording may change
errorstringrequiredStable machine-readable code (new codes may be added; fall back to the HTTP status)
one of"INVALID_REQUEST","UNAUTHORIZED","MISSING_AUTHENTICATION","FORBIDDEN","REQUEST_REJECTED","NOT_FOUND","ROUTE_NOT_FOUND","METHOD_NOT_ALLOWED","UPLOAD_INCOMPLETE","ALREADY_COMPLETED","ORIGINAL_UNAVAILABLE","EDIT_SOURCE_NOT_READY","NOT_AN_UPLOAD","IDEMPOTENCY_KEY_REUSED","IDEMPOTENCY_IN_PROGRESS","INVALID_STATE","TOO_LARGE","RATE_LIMITED","QUOTA_EXCEEDED","DAILY_LIMIT","VIDEOS_PAUSED","PLAN_UPGRADE_REQUIRED","PLAN_LIMIT_REACHED","INVALID_CODE","TERMS_REQUIRED","SESSION_REQUIRED","ACCOUNT_DISABLED","EMAIL_NOT_CONFIGURED","EMAIL_FAILED","UPSTREAM_ERROR","UNAVAILABLE"requestIdstringrequiredQuote this when reporting a problem
detailsarray of objectValidation errors only (400 INVALID_REQUEST) - every problem found, with its field path
fieldstringmessagestring
503UNAVAILABLE: temporary failure; retry with backoff
{
"message": "Daily limit reached (50 videos per 24 hours); try again later",
"error": "DAILY_LIMIT",
"requestId": "string",
"details": [
{
"field": "edit.clips[0].end",
"message": "80s is past the end of video …, which is 60.0s (1:00); use null for \"to the end\""
}
]
}Schema
messagestringrequiredHuman-readable; wording may change
errorstringrequiredStable machine-readable code (new codes may be added; fall back to the HTTP status)
one of"INVALID_REQUEST","UNAUTHORIZED","MISSING_AUTHENTICATION","FORBIDDEN","REQUEST_REJECTED","NOT_FOUND","ROUTE_NOT_FOUND","METHOD_NOT_ALLOWED","UPLOAD_INCOMPLETE","ALREADY_COMPLETED","ORIGINAL_UNAVAILABLE","EDIT_SOURCE_NOT_READY","NOT_AN_UPLOAD","IDEMPOTENCY_KEY_REUSED","IDEMPOTENCY_IN_PROGRESS","INVALID_STATE","TOO_LARGE","RATE_LIMITED","QUOTA_EXCEEDED","DAILY_LIMIT","VIDEOS_PAUSED","PLAN_UPGRADE_REQUIRED","PLAN_LIMIT_REACHED","INVALID_CODE","TERMS_REQUIRED","SESSION_REQUIRED","ACCOUNT_DISABLED","EMAIL_NOT_CONFIGURED","EMAIL_FAILED","UPSTREAM_ERROR","UNAVAILABLE"requestIdstringrequiredQuote this when reporting a problem
detailsarray of objectValidation errors only (400 INVALID_REQUEST) - every problem found, with its field path
fieldstringmessagestring
operationId startSignIn
POST/auth/verifyVerify a sign-in code (public; no auth required)
On first sign-in (new account), acceptTerms must be true. Returns a session token valid for 30 days.
Request body required
{
"email": "user@example.com",
"code": "123456",
"acceptTerms": false
}Schema
emailstringrequiredcodestringrequiredpattern"^\\d{6}$"acceptTermsbooleanRequired to create an account
Responses
200Verified
{
"sessionToken": "vps_...",
"expiresAt": 0,
"created": false,
"account": {
"email": "string",
"plan": "string",
"user": "string"
}
}Schema
sessionTokenstringexpiresAtintegerUnix time
createdbooleanTrue if a new account was created
accountobjectemailstringplanstringuserstring
400INVALID_REQUEST: invalid body or parameter (the message names the field)
{
"message": "Daily limit reached (50 videos per 24 hours); try again later",
"error": "DAILY_LIMIT",
"requestId": "string",
"details": [
{
"field": "edit.clips[0].end",
"message": "80s is past the end of video …, which is 60.0s (1:00); use null for \"to the end\""
}
]
}Schema
messagestringrequiredHuman-readable; wording may change
errorstringrequiredStable machine-readable code (new codes may be added; fall back to the HTTP status)
one of"INVALID_REQUEST","UNAUTHORIZED","MISSING_AUTHENTICATION","FORBIDDEN","REQUEST_REJECTED","NOT_FOUND","ROUTE_NOT_FOUND","METHOD_NOT_ALLOWED","UPLOAD_INCOMPLETE","ALREADY_COMPLETED","ORIGINAL_UNAVAILABLE","EDIT_SOURCE_NOT_READY","NOT_AN_UPLOAD","IDEMPOTENCY_KEY_REUSED","IDEMPOTENCY_IN_PROGRESS","INVALID_STATE","TOO_LARGE","RATE_LIMITED","QUOTA_EXCEEDED","DAILY_LIMIT","VIDEOS_PAUSED","PLAN_UPGRADE_REQUIRED","PLAN_LIMIT_REACHED","INVALID_CODE","TERMS_REQUIRED","SESSION_REQUIRED","ACCOUNT_DISABLED","EMAIL_NOT_CONFIGURED","EMAIL_FAILED","UPSTREAM_ERROR","UNAVAILABLE"requestIdstringrequiredQuote this when reporting a problem
detailsarray of objectValidation errors only (400 INVALID_REQUEST) - every problem found, with its field path
fieldstringmessagestring
429Rate limited
{
"message": "Daily limit reached (50 videos per 24 hours); try again later",
"error": "DAILY_LIMIT",
"requestId": "string",
"details": [
{
"field": "edit.clips[0].end",
"message": "80s is past the end of video …, which is 60.0s (1:00); use null for \"to the end\""
}
]
}Schema
messagestringrequiredHuman-readable; wording may change
errorstringrequiredStable machine-readable code (new codes may be added; fall back to the HTTP status)
one of"INVALID_REQUEST","UNAUTHORIZED","MISSING_AUTHENTICATION","FORBIDDEN","REQUEST_REJECTED","NOT_FOUND","ROUTE_NOT_FOUND","METHOD_NOT_ALLOWED","UPLOAD_INCOMPLETE","ALREADY_COMPLETED","ORIGINAL_UNAVAILABLE","EDIT_SOURCE_NOT_READY","NOT_AN_UPLOAD","IDEMPOTENCY_KEY_REUSED","IDEMPOTENCY_IN_PROGRESS","INVALID_STATE","TOO_LARGE","RATE_LIMITED","QUOTA_EXCEEDED","DAILY_LIMIT","VIDEOS_PAUSED","PLAN_UPGRADE_REQUIRED","PLAN_LIMIT_REACHED","INVALID_CODE","TERMS_REQUIRED","SESSION_REQUIRED","ACCOUNT_DISABLED","EMAIL_NOT_CONFIGURED","EMAIL_FAILED","UPSTREAM_ERROR","UNAVAILABLE"requestIdstringrequiredQuote this when reporting a problem
detailsarray of objectValidation errors only (400 INVALID_REQUEST) - every problem found, with its field path
fieldstringmessagestring
503UNAVAILABLE: temporary failure; retry with backoff
{
"message": "Daily limit reached (50 videos per 24 hours); try again later",
"error": "DAILY_LIMIT",
"requestId": "string",
"details": [
{
"field": "edit.clips[0].end",
"message": "80s is past the end of video …, which is 60.0s (1:00); use null for \"to the end\""
}
]
}Schema
messagestringrequiredHuman-readable; wording may change
errorstringrequiredStable machine-readable code (new codes may be added; fall back to the HTTP status)
one of"INVALID_REQUEST","UNAUTHORIZED","MISSING_AUTHENTICATION","FORBIDDEN","REQUEST_REJECTED","NOT_FOUND","ROUTE_NOT_FOUND","METHOD_NOT_ALLOWED","UPLOAD_INCOMPLETE","ALREADY_COMPLETED","ORIGINAL_UNAVAILABLE","EDIT_SOURCE_NOT_READY","NOT_AN_UPLOAD","IDEMPOTENCY_KEY_REUSED","IDEMPOTENCY_IN_PROGRESS","INVALID_STATE","TOO_LARGE","RATE_LIMITED","QUOTA_EXCEEDED","DAILY_LIMIT","VIDEOS_PAUSED","PLAN_UPGRADE_REQUIRED","PLAN_LIMIT_REACHED","INVALID_CODE","TERMS_REQUIRED","SESSION_REQUIRED","ACCOUNT_DISABLED","EMAIL_NOT_CONFIGURED","EMAIL_FAILED","UPSTREAM_ERROR","UNAVAILABLE"requestIdstringrequiredQuote this when reporting a problem
detailsarray of objectValidation errors only (400 INVALID_REQUEST) - every problem found, with its field path
fieldstringmessagestring
operationId verifySignIn
POST/auth/signoutEnd a session (session token only)
Responses
200Signed out
{
"signedOut": false
}Schema
signedOutboolean
401UNAUTHORIZED: missing, unknown or revoked API key
{
"message": "Daily limit reached (50 videos per 24 hours); try again later",
"error": "DAILY_LIMIT",
"requestId": "string",
"details": [
{
"field": "edit.clips[0].end",
"message": "80s is past the end of video …, which is 60.0s (1:00); use null for \"to the end\""
}
]
}Schema
messagestringrequiredHuman-readable; wording may change
errorstringrequiredStable machine-readable code (new codes may be added; fall back to the HTTP status)
one of"INVALID_REQUEST","UNAUTHORIZED","MISSING_AUTHENTICATION","FORBIDDEN","REQUEST_REJECTED","NOT_FOUND","ROUTE_NOT_FOUND","METHOD_NOT_ALLOWED","UPLOAD_INCOMPLETE","ALREADY_COMPLETED","ORIGINAL_UNAVAILABLE","EDIT_SOURCE_NOT_READY","NOT_AN_UPLOAD","IDEMPOTENCY_KEY_REUSED","IDEMPOTENCY_IN_PROGRESS","INVALID_STATE","TOO_LARGE","RATE_LIMITED","QUOTA_EXCEEDED","DAILY_LIMIT","VIDEOS_PAUSED","PLAN_UPGRADE_REQUIRED","PLAN_LIMIT_REACHED","INVALID_CODE","TERMS_REQUIRED","SESSION_REQUIRED","ACCOUNT_DISABLED","EMAIL_NOT_CONFIGURED","EMAIL_FAILED","UPSTREAM_ERROR","UNAVAILABLE"requestIdstringrequiredQuote this when reporting a problem
detailsarray of objectValidation errors only (400 INVALID_REQUEST) - every problem found, with its field path
fieldstringmessagestring
429RATE_LIMITED (retry with backoff), QUOTA_EXCEEDED, DAILY_LIMIT or VIDEOS_PAUSED
{
"message": "Daily limit reached (50 videos per 24 hours); try again later",
"error": "DAILY_LIMIT",
"requestId": "string",
"details": [
{
"field": "edit.clips[0].end",
"message": "80s is past the end of video …, which is 60.0s (1:00); use null for \"to the end\""
}
]
}Schema
messagestringrequiredHuman-readable; wording may change
errorstringrequiredStable machine-readable code (new codes may be added; fall back to the HTTP status)
one of"INVALID_REQUEST","UNAUTHORIZED","MISSING_AUTHENTICATION","FORBIDDEN","REQUEST_REJECTED","NOT_FOUND","ROUTE_NOT_FOUND","METHOD_NOT_ALLOWED","UPLOAD_INCOMPLETE","ALREADY_COMPLETED","ORIGINAL_UNAVAILABLE","EDIT_SOURCE_NOT_READY","NOT_AN_UPLOAD","IDEMPOTENCY_KEY_REUSED","IDEMPOTENCY_IN_PROGRESS","INVALID_STATE","TOO_LARGE","RATE_LIMITED","QUOTA_EXCEEDED","DAILY_LIMIT","VIDEOS_PAUSED","PLAN_UPGRADE_REQUIRED","PLAN_LIMIT_REACHED","INVALID_CODE","TERMS_REQUIRED","SESSION_REQUIRED","ACCOUNT_DISABLED","EMAIL_NOT_CONFIGURED","EMAIL_FAILED","UPSTREAM_ERROR","UNAVAILABLE"requestIdstringrequiredQuote this when reporting a problem
detailsarray of objectValidation errors only (400 INVALID_REQUEST) - every problem found, with its field path
fieldstringmessagestring
503UNAVAILABLE: temporary failure; retry with backoff
{
"message": "Daily limit reached (50 videos per 24 hours); try again later",
"error": "DAILY_LIMIT",
"requestId": "string",
"details": [
{
"field": "edit.clips[0].end",
"message": "80s is past the end of video …, which is 60.0s (1:00); use null for \"to the end\""
}
]
}Schema
messagestringrequiredHuman-readable; wording may change
errorstringrequiredStable machine-readable code (new codes may be added; fall back to the HTTP status)
one of"INVALID_REQUEST","UNAUTHORIZED","MISSING_AUTHENTICATION","FORBIDDEN","REQUEST_REJECTED","NOT_FOUND","ROUTE_NOT_FOUND","METHOD_NOT_ALLOWED","UPLOAD_INCOMPLETE","ALREADY_COMPLETED","ORIGINAL_UNAVAILABLE","EDIT_SOURCE_NOT_READY","NOT_AN_UPLOAD","IDEMPOTENCY_KEY_REUSED","IDEMPOTENCY_IN_PROGRESS","INVALID_STATE","TOO_LARGE","RATE_LIMITED","QUOTA_EXCEEDED","DAILY_LIMIT","VIDEOS_PAUSED","PLAN_UPGRADE_REQUIRED","PLAN_LIMIT_REACHED","INVALID_CODE","TERMS_REQUIRED","SESSION_REQUIRED","ACCOUNT_DISABLED","EMAIL_NOT_CONFIGURED","EMAIL_FAILED","UPSTREAM_ERROR","UNAVAILABLE"requestIdstringrequiredQuote this when reporting a problem
detailsarray of objectValidation errors only (400 INVALID_REQUEST) - every problem found, with its field path
fieldstringmessagestring
operationId signOut
GET/accountYour account info, plan and this month's usage
Responses
200Account details
{
"email": "string",
"user": "string",
"plan": "string",
"planName": "string",
"createdAt": "string",
"limits": {
"minutes": 0,
"storageGb": 0,
"maxHeight": 720,
"features": [
"string"
],
"videosPerDay": 0,
"apiKeys": 0,
"overage": false
},
"usage": {
"month": "2026-10",
"billableMinutes": 0,
"videos": 0,
"storedGb": 0,
"gbMonths": 0
},
"session": false,
"billing": {
"customer": false
}
}Schema
emailstringuserstringplanstringplanNamestringcreatedAtstring (date-time)limitsobjectminutesintegerMonthly processing minutes (billable)
storageGbintegerMonthly storage in GB
maxHeightintegerone of720,1080,1440,2160featuresarray of stringvideosPerDayintegerapiKeysintegerMax simultaneous API keys
overagebooleanWhether overage is billed
usageobjectmonthstringbillableMinutesnumbervideosintegerstoredGbnumbergbMonthsnumber
sessionbooleanTrue if authenticated with a session token
billingobjectcustomerbooleanHas a Stripe customer ID
401UNAUTHORIZED: missing, unknown or revoked API key
{
"message": "Daily limit reached (50 videos per 24 hours); try again later",
"error": "DAILY_LIMIT",
"requestId": "string",
"details": [
{
"field": "edit.clips[0].end",
"message": "80s is past the end of video …, which is 60.0s (1:00); use null for \"to the end\""
}
]
}Schema
messagestringrequiredHuman-readable; wording may change
errorstringrequiredStable machine-readable code (new codes may be added; fall back to the HTTP status)
one of"INVALID_REQUEST","UNAUTHORIZED","MISSING_AUTHENTICATION","FORBIDDEN","REQUEST_REJECTED","NOT_FOUND","ROUTE_NOT_FOUND","METHOD_NOT_ALLOWED","UPLOAD_INCOMPLETE","ALREADY_COMPLETED","ORIGINAL_UNAVAILABLE","EDIT_SOURCE_NOT_READY","NOT_AN_UPLOAD","IDEMPOTENCY_KEY_REUSED","IDEMPOTENCY_IN_PROGRESS","INVALID_STATE","TOO_LARGE","RATE_LIMITED","QUOTA_EXCEEDED","DAILY_LIMIT","VIDEOS_PAUSED","PLAN_UPGRADE_REQUIRED","PLAN_LIMIT_REACHED","INVALID_CODE","TERMS_REQUIRED","SESSION_REQUIRED","ACCOUNT_DISABLED","EMAIL_NOT_CONFIGURED","EMAIL_FAILED","UPSTREAM_ERROR","UNAVAILABLE"requestIdstringrequiredQuote this when reporting a problem
detailsarray of objectValidation errors only (400 INVALID_REQUEST) - every problem found, with its field path
fieldstringmessagestring
429RATE_LIMITED (retry with backoff), QUOTA_EXCEEDED, DAILY_LIMIT or VIDEOS_PAUSED
{
"message": "Daily limit reached (50 videos per 24 hours); try again later",
"error": "DAILY_LIMIT",
"requestId": "string",
"details": [
{
"field": "edit.clips[0].end",
"message": "80s is past the end of video …, which is 60.0s (1:00); use null for \"to the end\""
}
]
}Schema
messagestringrequiredHuman-readable; wording may change
errorstringrequiredStable machine-readable code (new codes may be added; fall back to the HTTP status)
one of"INVALID_REQUEST","UNAUTHORIZED","MISSING_AUTHENTICATION","FORBIDDEN","REQUEST_REJECTED","NOT_FOUND","ROUTE_NOT_FOUND","METHOD_NOT_ALLOWED","UPLOAD_INCOMPLETE","ALREADY_COMPLETED","ORIGINAL_UNAVAILABLE","EDIT_SOURCE_NOT_READY","NOT_AN_UPLOAD","IDEMPOTENCY_KEY_REUSED","IDEMPOTENCY_IN_PROGRESS","INVALID_STATE","TOO_LARGE","RATE_LIMITED","QUOTA_EXCEEDED","DAILY_LIMIT","VIDEOS_PAUSED","PLAN_UPGRADE_REQUIRED","PLAN_LIMIT_REACHED","INVALID_CODE","TERMS_REQUIRED","SESSION_REQUIRED","ACCOUNT_DISABLED","EMAIL_NOT_CONFIGURED","EMAIL_FAILED","UPSTREAM_ERROR","UNAVAILABLE"requestIdstringrequiredQuote this when reporting a problem
detailsarray of objectValidation errors only (400 INVALID_REQUEST) - every problem found, with its field path
fieldstringmessagestring
503UNAVAILABLE: temporary failure; retry with backoff
{
"message": "Daily limit reached (50 videos per 24 hours); try again later",
"error": "DAILY_LIMIT",
"requestId": "string",
"details": [
{
"field": "edit.clips[0].end",
"message": "80s is past the end of video …, which is 60.0s (1:00); use null for \"to the end\""
}
]
}Schema
messagestringrequiredHuman-readable; wording may change
errorstringrequiredStable machine-readable code (new codes may be added; fall back to the HTTP status)
one of"INVALID_REQUEST","UNAUTHORIZED","MISSING_AUTHENTICATION","FORBIDDEN","REQUEST_REJECTED","NOT_FOUND","ROUTE_NOT_FOUND","METHOD_NOT_ALLOWED","UPLOAD_INCOMPLETE","ALREADY_COMPLETED","ORIGINAL_UNAVAILABLE","EDIT_SOURCE_NOT_READY","NOT_AN_UPLOAD","IDEMPOTENCY_KEY_REUSED","IDEMPOTENCY_IN_PROGRESS","INVALID_STATE","TOO_LARGE","RATE_LIMITED","QUOTA_EXCEEDED","DAILY_LIMIT","VIDEOS_PAUSED","PLAN_UPGRADE_REQUIRED","PLAN_LIMIT_REACHED","INVALID_CODE","TERMS_REQUIRED","SESSION_REQUIRED","ACCOUNT_DISABLED","EMAIL_NOT_CONFIGURED","EMAIL_FAILED","UPSTREAM_ERROR","UNAVAILABLE"requestIdstringrequiredQuote this when reporting a problem
detailsarray of objectValidation errors only (400 INVALID_REQUEST) - every problem found, with its field path
fieldstringmessagestring
operationId getAccount
GET/account/keysList your API keys (never the keys themselves)
Responses
200API keys
{
"keys": [
{
"keyId": "string",
"label": "string",
"createdAt": "string",
"revoked": false,
"prefix": "string"
}
]
}Schema
keysarray of objectkeyIdstringlabelstringcreatedAtstring (date-time)revokedbooleanprefixstringFirst 8 chars of the key
401UNAUTHORIZED: missing, unknown or revoked API key
{
"message": "Daily limit reached (50 videos per 24 hours); try again later",
"error": "DAILY_LIMIT",
"requestId": "string",
"details": [
{
"field": "edit.clips[0].end",
"message": "80s is past the end of video …, which is 60.0s (1:00); use null for \"to the end\""
}
]
}Schema
messagestringrequiredHuman-readable; wording may change
errorstringrequiredStable machine-readable code (new codes may be added; fall back to the HTTP status)
one of"INVALID_REQUEST","UNAUTHORIZED","MISSING_AUTHENTICATION","FORBIDDEN","REQUEST_REJECTED","NOT_FOUND","ROUTE_NOT_FOUND","METHOD_NOT_ALLOWED","UPLOAD_INCOMPLETE","ALREADY_COMPLETED","ORIGINAL_UNAVAILABLE","EDIT_SOURCE_NOT_READY","NOT_AN_UPLOAD","IDEMPOTENCY_KEY_REUSED","IDEMPOTENCY_IN_PROGRESS","INVALID_STATE","TOO_LARGE","RATE_LIMITED","QUOTA_EXCEEDED","DAILY_LIMIT","VIDEOS_PAUSED","PLAN_UPGRADE_REQUIRED","PLAN_LIMIT_REACHED","INVALID_CODE","TERMS_REQUIRED","SESSION_REQUIRED","ACCOUNT_DISABLED","EMAIL_NOT_CONFIGURED","EMAIL_FAILED","UPSTREAM_ERROR","UNAVAILABLE"requestIdstringrequiredQuote this when reporting a problem
detailsarray of objectValidation errors only (400 INVALID_REQUEST) - every problem found, with its field path
fieldstringmessagestring
429RATE_LIMITED (retry with backoff), QUOTA_EXCEEDED, DAILY_LIMIT or VIDEOS_PAUSED
{
"message": "Daily limit reached (50 videos per 24 hours); try again later",
"error": "DAILY_LIMIT",
"requestId": "string",
"details": [
{
"field": "edit.clips[0].end",
"message": "80s is past the end of video …, which is 60.0s (1:00); use null for \"to the end\""
}
]
}Schema
messagestringrequiredHuman-readable; wording may change
errorstringrequiredStable machine-readable code (new codes may be added; fall back to the HTTP status)
one of"INVALID_REQUEST","UNAUTHORIZED","MISSING_AUTHENTICATION","FORBIDDEN","REQUEST_REJECTED","NOT_FOUND","ROUTE_NOT_FOUND","METHOD_NOT_ALLOWED","UPLOAD_INCOMPLETE","ALREADY_COMPLETED","ORIGINAL_UNAVAILABLE","EDIT_SOURCE_NOT_READY","NOT_AN_UPLOAD","IDEMPOTENCY_KEY_REUSED","IDEMPOTENCY_IN_PROGRESS","INVALID_STATE","TOO_LARGE","RATE_LIMITED","QUOTA_EXCEEDED","DAILY_LIMIT","VIDEOS_PAUSED","PLAN_UPGRADE_REQUIRED","PLAN_LIMIT_REACHED","INVALID_CODE","TERMS_REQUIRED","SESSION_REQUIRED","ACCOUNT_DISABLED","EMAIL_NOT_CONFIGURED","EMAIL_FAILED","UPSTREAM_ERROR","UNAVAILABLE"requestIdstringrequiredQuote this when reporting a problem
detailsarray of objectValidation errors only (400 INVALID_REQUEST) - every problem found, with its field path
fieldstringmessagestring
503UNAVAILABLE: temporary failure; retry with backoff
{
"message": "Daily limit reached (50 videos per 24 hours); try again later",
"error": "DAILY_LIMIT",
"requestId": "string",
"details": [
{
"field": "edit.clips[0].end",
"message": "80s is past the end of video …, which is 60.0s (1:00); use null for \"to the end\""
}
]
}Schema
messagestringrequiredHuman-readable; wording may change
errorstringrequiredStable machine-readable code (new codes may be added; fall back to the HTTP status)
one of"INVALID_REQUEST","UNAUTHORIZED","MISSING_AUTHENTICATION","FORBIDDEN","REQUEST_REJECTED","NOT_FOUND","ROUTE_NOT_FOUND","METHOD_NOT_ALLOWED","UPLOAD_INCOMPLETE","ALREADY_COMPLETED","ORIGINAL_UNAVAILABLE","EDIT_SOURCE_NOT_READY","NOT_AN_UPLOAD","IDEMPOTENCY_KEY_REUSED","IDEMPOTENCY_IN_PROGRESS","INVALID_STATE","TOO_LARGE","RATE_LIMITED","QUOTA_EXCEEDED","DAILY_LIMIT","VIDEOS_PAUSED","PLAN_UPGRADE_REQUIRED","PLAN_LIMIT_REACHED","INVALID_CODE","TERMS_REQUIRED","SESSION_REQUIRED","ACCOUNT_DISABLED","EMAIL_NOT_CONFIGURED","EMAIL_FAILED","UPSTREAM_ERROR","UNAVAILABLE"requestIdstringrequiredQuote this when reporting a problem
detailsarray of objectValidation errors only (400 INVALID_REQUEST) - every problem found, with its field path
fieldstringmessagestring
operationId listApiKeys
POST/account/keysCreate an API key (session token only)
Request body required
{
"label": "string"
}Schema
labelstringOptional label for the key
max length60
Responses
201Key created
{
"key": "string",
"keyId": "string",
"label": "string",
"note": "string"
}Schema
keystringThe full key (shown only once)
keyIdstringlabelstringnotestringCopy it now: it can't be shown again
401UNAUTHORIZED: missing, unknown or revoked API key
{
"message": "Daily limit reached (50 videos per 24 hours); try again later",
"error": "DAILY_LIMIT",
"requestId": "string",
"details": [
{
"field": "edit.clips[0].end",
"message": "80s is past the end of video …, which is 60.0s (1:00); use null for \"to the end\""
}
]
}Schema
messagestringrequiredHuman-readable; wording may change
errorstringrequiredStable machine-readable code (new codes may be added; fall back to the HTTP status)
one of"INVALID_REQUEST","UNAUTHORIZED","MISSING_AUTHENTICATION","FORBIDDEN","REQUEST_REJECTED","NOT_FOUND","ROUTE_NOT_FOUND","METHOD_NOT_ALLOWED","UPLOAD_INCOMPLETE","ALREADY_COMPLETED","ORIGINAL_UNAVAILABLE","EDIT_SOURCE_NOT_READY","NOT_AN_UPLOAD","IDEMPOTENCY_KEY_REUSED","IDEMPOTENCY_IN_PROGRESS","INVALID_STATE","TOO_LARGE","RATE_LIMITED","QUOTA_EXCEEDED","DAILY_LIMIT","VIDEOS_PAUSED","PLAN_UPGRADE_REQUIRED","PLAN_LIMIT_REACHED","INVALID_CODE","TERMS_REQUIRED","SESSION_REQUIRED","ACCOUNT_DISABLED","EMAIL_NOT_CONFIGURED","EMAIL_FAILED","UPSTREAM_ERROR","UNAVAILABLE"requestIdstringrequiredQuote this when reporting a problem
detailsarray of objectValidation errors only (400 INVALID_REQUEST) - every problem found, with its field path
fieldstringmessagestring
403Session required or plan limit reached
{
"message": "Daily limit reached (50 videos per 24 hours); try again later",
"error": "DAILY_LIMIT",
"requestId": "string",
"details": [
{
"field": "edit.clips[0].end",
"message": "80s is past the end of video …, which is 60.0s (1:00); use null for \"to the end\""
}
]
}Schema
messagestringrequiredHuman-readable; wording may change
errorstringrequiredStable machine-readable code (new codes may be added; fall back to the HTTP status)
one of"INVALID_REQUEST","UNAUTHORIZED","MISSING_AUTHENTICATION","FORBIDDEN","REQUEST_REJECTED","NOT_FOUND","ROUTE_NOT_FOUND","METHOD_NOT_ALLOWED","UPLOAD_INCOMPLETE","ALREADY_COMPLETED","ORIGINAL_UNAVAILABLE","EDIT_SOURCE_NOT_READY","NOT_AN_UPLOAD","IDEMPOTENCY_KEY_REUSED","IDEMPOTENCY_IN_PROGRESS","INVALID_STATE","TOO_LARGE","RATE_LIMITED","QUOTA_EXCEEDED","DAILY_LIMIT","VIDEOS_PAUSED","PLAN_UPGRADE_REQUIRED","PLAN_LIMIT_REACHED","INVALID_CODE","TERMS_REQUIRED","SESSION_REQUIRED","ACCOUNT_DISABLED","EMAIL_NOT_CONFIGURED","EMAIL_FAILED","UPSTREAM_ERROR","UNAVAILABLE"requestIdstringrequiredQuote this when reporting a problem
detailsarray of objectValidation errors only (400 INVALID_REQUEST) - every problem found, with its field path
fieldstringmessagestring
429RATE_LIMITED (retry with backoff), QUOTA_EXCEEDED, DAILY_LIMIT or VIDEOS_PAUSED
{
"message": "Daily limit reached (50 videos per 24 hours); try again later",
"error": "DAILY_LIMIT",
"requestId": "string",
"details": [
{
"field": "edit.clips[0].end",
"message": "80s is past the end of video …, which is 60.0s (1:00); use null for \"to the end\""
}
]
}Schema
messagestringrequiredHuman-readable; wording may change
errorstringrequiredStable machine-readable code (new codes may be added; fall back to the HTTP status)
one of"INVALID_REQUEST","UNAUTHORIZED","MISSING_AUTHENTICATION","FORBIDDEN","REQUEST_REJECTED","NOT_FOUND","ROUTE_NOT_FOUND","METHOD_NOT_ALLOWED","UPLOAD_INCOMPLETE","ALREADY_COMPLETED","ORIGINAL_UNAVAILABLE","EDIT_SOURCE_NOT_READY","NOT_AN_UPLOAD","IDEMPOTENCY_KEY_REUSED","IDEMPOTENCY_IN_PROGRESS","INVALID_STATE","TOO_LARGE","RATE_LIMITED","QUOTA_EXCEEDED","DAILY_LIMIT","VIDEOS_PAUSED","PLAN_UPGRADE_REQUIRED","PLAN_LIMIT_REACHED","INVALID_CODE","TERMS_REQUIRED","SESSION_REQUIRED","ACCOUNT_DISABLED","EMAIL_NOT_CONFIGURED","EMAIL_FAILED","UPSTREAM_ERROR","UNAVAILABLE"requestIdstringrequiredQuote this when reporting a problem
detailsarray of objectValidation errors only (400 INVALID_REQUEST) - every problem found, with its field path
fieldstringmessagestring
503UNAVAILABLE: temporary failure; retry with backoff
{
"message": "Daily limit reached (50 videos per 24 hours); try again later",
"error": "DAILY_LIMIT",
"requestId": "string",
"details": [
{
"field": "edit.clips[0].end",
"message": "80s is past the end of video …, which is 60.0s (1:00); use null for \"to the end\""
}
]
}Schema
messagestringrequiredHuman-readable; wording may change
errorstringrequiredStable machine-readable code (new codes may be added; fall back to the HTTP status)
one of"INVALID_REQUEST","UNAUTHORIZED","MISSING_AUTHENTICATION","FORBIDDEN","REQUEST_REJECTED","NOT_FOUND","ROUTE_NOT_FOUND","METHOD_NOT_ALLOWED","UPLOAD_INCOMPLETE","ALREADY_COMPLETED","ORIGINAL_UNAVAILABLE","EDIT_SOURCE_NOT_READY","NOT_AN_UPLOAD","IDEMPOTENCY_KEY_REUSED","IDEMPOTENCY_IN_PROGRESS","INVALID_STATE","TOO_LARGE","RATE_LIMITED","QUOTA_EXCEEDED","DAILY_LIMIT","VIDEOS_PAUSED","PLAN_UPGRADE_REQUIRED","PLAN_LIMIT_REACHED","INVALID_CODE","TERMS_REQUIRED","SESSION_REQUIRED","ACCOUNT_DISABLED","EMAIL_NOT_CONFIGURED","EMAIL_FAILED","UPSTREAM_ERROR","UNAVAILABLE"requestIdstringrequiredQuote this when reporting a problem
detailsarray of objectValidation errors only (400 INVALID_REQUEST) - every problem found, with its field path
fieldstringmessagestring
operationId createApiKey
DELETE/account/keys/{keyId}Revoke an API key (session token only)
Parameters
| Name | In | Type | Description |
|---|---|---|---|
keyIdrequired | path | string |
Responses
200Revoked
{
"keyId": "string",
"revoked": false
}Schema
keyIdstringrevokedboolean
401UNAUTHORIZED: missing, unknown or revoked API key
{
"message": "Daily limit reached (50 videos per 24 hours); try again later",
"error": "DAILY_LIMIT",
"requestId": "string",
"details": [
{
"field": "edit.clips[0].end",
"message": "80s is past the end of video …, which is 60.0s (1:00); use null for \"to the end\""
}
]
}Schema
messagestringrequiredHuman-readable; wording may change
errorstringrequiredStable machine-readable code (new codes may be added; fall back to the HTTP status)
one of"INVALID_REQUEST","UNAUTHORIZED","MISSING_AUTHENTICATION","FORBIDDEN","REQUEST_REJECTED","NOT_FOUND","ROUTE_NOT_FOUND","METHOD_NOT_ALLOWED","UPLOAD_INCOMPLETE","ALREADY_COMPLETED","ORIGINAL_UNAVAILABLE","EDIT_SOURCE_NOT_READY","NOT_AN_UPLOAD","IDEMPOTENCY_KEY_REUSED","IDEMPOTENCY_IN_PROGRESS","INVALID_STATE","TOO_LARGE","RATE_LIMITED","QUOTA_EXCEEDED","DAILY_LIMIT","VIDEOS_PAUSED","PLAN_UPGRADE_REQUIRED","PLAN_LIMIT_REACHED","INVALID_CODE","TERMS_REQUIRED","SESSION_REQUIRED","ACCOUNT_DISABLED","EMAIL_NOT_CONFIGURED","EMAIL_FAILED","UPSTREAM_ERROR","UNAVAILABLE"requestIdstringrequiredQuote this when reporting a problem
detailsarray of objectValidation errors only (400 INVALID_REQUEST) - every problem found, with its field path
fieldstringmessagestring
403Session required
{
"message": "Daily limit reached (50 videos per 24 hours); try again later",
"error": "DAILY_LIMIT",
"requestId": "string",
"details": [
{
"field": "edit.clips[0].end",
"message": "80s is past the end of video …, which is 60.0s (1:00); use null for \"to the end\""
}
]
}Schema
messagestringrequiredHuman-readable; wording may change
errorstringrequiredStable machine-readable code (new codes may be added; fall back to the HTTP status)
one of"INVALID_REQUEST","UNAUTHORIZED","MISSING_AUTHENTICATION","FORBIDDEN","REQUEST_REJECTED","NOT_FOUND","ROUTE_NOT_FOUND","METHOD_NOT_ALLOWED","UPLOAD_INCOMPLETE","ALREADY_COMPLETED","ORIGINAL_UNAVAILABLE","EDIT_SOURCE_NOT_READY","NOT_AN_UPLOAD","IDEMPOTENCY_KEY_REUSED","IDEMPOTENCY_IN_PROGRESS","INVALID_STATE","TOO_LARGE","RATE_LIMITED","QUOTA_EXCEEDED","DAILY_LIMIT","VIDEOS_PAUSED","PLAN_UPGRADE_REQUIRED","PLAN_LIMIT_REACHED","INVALID_CODE","TERMS_REQUIRED","SESSION_REQUIRED","ACCOUNT_DISABLED","EMAIL_NOT_CONFIGURED","EMAIL_FAILED","UPSTREAM_ERROR","UNAVAILABLE"requestIdstringrequiredQuote this when reporting a problem
detailsarray of objectValidation errors only (400 INVALID_REQUEST) - every problem found, with its field path
fieldstringmessagestring
404NOT_FOUND: no such video for this user (wrong ID or someone else's)
{
"message": "Daily limit reached (50 videos per 24 hours); try again later",
"error": "DAILY_LIMIT",
"requestId": "string",
"details": [
{
"field": "edit.clips[0].end",
"message": "80s is past the end of video …, which is 60.0s (1:00); use null for \"to the end\""
}
]
}Schema
messagestringrequiredHuman-readable; wording may change
errorstringrequiredStable machine-readable code (new codes may be added; fall back to the HTTP status)
one of"INVALID_REQUEST","UNAUTHORIZED","MISSING_AUTHENTICATION","FORBIDDEN","REQUEST_REJECTED","NOT_FOUND","ROUTE_NOT_FOUND","METHOD_NOT_ALLOWED","UPLOAD_INCOMPLETE","ALREADY_COMPLETED","ORIGINAL_UNAVAILABLE","EDIT_SOURCE_NOT_READY","NOT_AN_UPLOAD","IDEMPOTENCY_KEY_REUSED","IDEMPOTENCY_IN_PROGRESS","INVALID_STATE","TOO_LARGE","RATE_LIMITED","QUOTA_EXCEEDED","DAILY_LIMIT","VIDEOS_PAUSED","PLAN_UPGRADE_REQUIRED","PLAN_LIMIT_REACHED","INVALID_CODE","TERMS_REQUIRED","SESSION_REQUIRED","ACCOUNT_DISABLED","EMAIL_NOT_CONFIGURED","EMAIL_FAILED","UPSTREAM_ERROR","UNAVAILABLE"requestIdstringrequiredQuote this when reporting a problem
detailsarray of objectValidation errors only (400 INVALID_REQUEST) - every problem found, with its field path
fieldstringmessagestring
429RATE_LIMITED (retry with backoff), QUOTA_EXCEEDED, DAILY_LIMIT or VIDEOS_PAUSED
{
"message": "Daily limit reached (50 videos per 24 hours); try again later",
"error": "DAILY_LIMIT",
"requestId": "string",
"details": [
{
"field": "edit.clips[0].end",
"message": "80s is past the end of video …, which is 60.0s (1:00); use null for \"to the end\""
}
]
}Schema
messagestringrequiredHuman-readable; wording may change
errorstringrequiredStable machine-readable code (new codes may be added; fall back to the HTTP status)
one of"INVALID_REQUEST","UNAUTHORIZED","MISSING_AUTHENTICATION","FORBIDDEN","REQUEST_REJECTED","NOT_FOUND","ROUTE_NOT_FOUND","METHOD_NOT_ALLOWED","UPLOAD_INCOMPLETE","ALREADY_COMPLETED","ORIGINAL_UNAVAILABLE","EDIT_SOURCE_NOT_READY","NOT_AN_UPLOAD","IDEMPOTENCY_KEY_REUSED","IDEMPOTENCY_IN_PROGRESS","INVALID_STATE","TOO_LARGE","RATE_LIMITED","QUOTA_EXCEEDED","DAILY_LIMIT","VIDEOS_PAUSED","PLAN_UPGRADE_REQUIRED","PLAN_LIMIT_REACHED","INVALID_CODE","TERMS_REQUIRED","SESSION_REQUIRED","ACCOUNT_DISABLED","EMAIL_NOT_CONFIGURED","EMAIL_FAILED","UPSTREAM_ERROR","UNAVAILABLE"requestIdstringrequiredQuote this when reporting a problem
detailsarray of objectValidation errors only (400 INVALID_REQUEST) - every problem found, with its field path
fieldstringmessagestring
503UNAVAILABLE: temporary failure; retry with backoff
{
"message": "Daily limit reached (50 videos per 24 hours); try again later",
"error": "DAILY_LIMIT",
"requestId": "string",
"details": [
{
"field": "edit.clips[0].end",
"message": "80s is past the end of video …, which is 60.0s (1:00); use null for \"to the end\""
}
]
}Schema
messagestringrequiredHuman-readable; wording may change
errorstringrequiredStable machine-readable code (new codes may be added; fall back to the HTTP status)
one of"INVALID_REQUEST","UNAUTHORIZED","MISSING_AUTHENTICATION","FORBIDDEN","REQUEST_REJECTED","NOT_FOUND","ROUTE_NOT_FOUND","METHOD_NOT_ALLOWED","UPLOAD_INCOMPLETE","ALREADY_COMPLETED","ORIGINAL_UNAVAILABLE","EDIT_SOURCE_NOT_READY","NOT_AN_UPLOAD","IDEMPOTENCY_KEY_REUSED","IDEMPOTENCY_IN_PROGRESS","INVALID_STATE","TOO_LARGE","RATE_LIMITED","QUOTA_EXCEEDED","DAILY_LIMIT","VIDEOS_PAUSED","PLAN_UPGRADE_REQUIRED","PLAN_LIMIT_REACHED","INVALID_CODE","TERMS_REQUIRED","SESSION_REQUIRED","ACCOUNT_DISABLED","EMAIL_NOT_CONFIGURED","EMAIL_FAILED","UPSTREAM_ERROR","UNAVAILABLE"requestIdstringrequiredQuote this when reporting a problem
detailsarray of objectValidation errors only (400 INVALID_REQUEST) - every problem found, with its field path
fieldstringmessagestring
operationId revokeApiKey
Webhooks
Set up an endpoint to receive events when videos finish, fail, or you reach usage thresholds. Each account has one webhook. Events are signed with Standard Webhooks. Events: video.ready (processing succeeded), video.failed, video.canceled, video.deleted, usage.threshold (80% and 100% of plan limits). Each event envelope contains id, type, createdAt and data. Signature verification uses HMAC-SHA256 of "id.timestamp.body" with the base64-decoded secret after "whsec_".
GET/webhookGet your webhook configuration
Responses
200Webhook config
{
"configured": false,
"url": "string",
"events": [
"video.ready"
],
"enabled": false,
"secret": "string",
"createdAt": "string",
"updatedAt": "string"
}Schema
configuredbooleanurlstringeventsarray of stringenabledbooleansecretstringwhsec_-prefixed signing secret
createdAtstring (date-time)updatedAtstring (date-time)
401UNAUTHORIZED: missing, unknown or revoked API key
{
"message": "Daily limit reached (50 videos per 24 hours); try again later",
"error": "DAILY_LIMIT",
"requestId": "string",
"details": [
{
"field": "edit.clips[0].end",
"message": "80s is past the end of video …, which is 60.0s (1:00); use null for \"to the end\""
}
]
}Schema
messagestringrequiredHuman-readable; wording may change
errorstringrequiredStable machine-readable code (new codes may be added; fall back to the HTTP status)
one of"INVALID_REQUEST","UNAUTHORIZED","MISSING_AUTHENTICATION","FORBIDDEN","REQUEST_REJECTED","NOT_FOUND","ROUTE_NOT_FOUND","METHOD_NOT_ALLOWED","UPLOAD_INCOMPLETE","ALREADY_COMPLETED","ORIGINAL_UNAVAILABLE","EDIT_SOURCE_NOT_READY","NOT_AN_UPLOAD","IDEMPOTENCY_KEY_REUSED","IDEMPOTENCY_IN_PROGRESS","INVALID_STATE","TOO_LARGE","RATE_LIMITED","QUOTA_EXCEEDED","DAILY_LIMIT","VIDEOS_PAUSED","PLAN_UPGRADE_REQUIRED","PLAN_LIMIT_REACHED","INVALID_CODE","TERMS_REQUIRED","SESSION_REQUIRED","ACCOUNT_DISABLED","EMAIL_NOT_CONFIGURED","EMAIL_FAILED","UPSTREAM_ERROR","UNAVAILABLE"requestIdstringrequiredQuote this when reporting a problem
detailsarray of objectValidation errors only (400 INVALID_REQUEST) - every problem found, with its field path
fieldstringmessagestring
429RATE_LIMITED (retry with backoff), QUOTA_EXCEEDED, DAILY_LIMIT or VIDEOS_PAUSED
{
"message": "Daily limit reached (50 videos per 24 hours); try again later",
"error": "DAILY_LIMIT",
"requestId": "string",
"details": [
{
"field": "edit.clips[0].end",
"message": "80s is past the end of video …, which is 60.0s (1:00); use null for \"to the end\""
}
]
}Schema
messagestringrequiredHuman-readable; wording may change
errorstringrequiredStable machine-readable code (new codes may be added; fall back to the HTTP status)
one of"INVALID_REQUEST","UNAUTHORIZED","MISSING_AUTHENTICATION","FORBIDDEN","REQUEST_REJECTED","NOT_FOUND","ROUTE_NOT_FOUND","METHOD_NOT_ALLOWED","UPLOAD_INCOMPLETE","ALREADY_COMPLETED","ORIGINAL_UNAVAILABLE","EDIT_SOURCE_NOT_READY","NOT_AN_UPLOAD","IDEMPOTENCY_KEY_REUSED","IDEMPOTENCY_IN_PROGRESS","INVALID_STATE","TOO_LARGE","RATE_LIMITED","QUOTA_EXCEEDED","DAILY_LIMIT","VIDEOS_PAUSED","PLAN_UPGRADE_REQUIRED","PLAN_LIMIT_REACHED","INVALID_CODE","TERMS_REQUIRED","SESSION_REQUIRED","ACCOUNT_DISABLED","EMAIL_NOT_CONFIGURED","EMAIL_FAILED","UPSTREAM_ERROR","UNAVAILABLE"requestIdstringrequiredQuote this when reporting a problem
detailsarray of objectValidation errors only (400 INVALID_REQUEST) - every problem found, with its field path
fieldstringmessagestring
503UNAVAILABLE: temporary failure; retry with backoff
{
"message": "Daily limit reached (50 videos per 24 hours); try again later",
"error": "DAILY_LIMIT",
"requestId": "string",
"details": [
{
"field": "edit.clips[0].end",
"message": "80s is past the end of video …, which is 60.0s (1:00); use null for \"to the end\""
}
]
}Schema
messagestringrequiredHuman-readable; wording may change
errorstringrequiredStable machine-readable code (new codes may be added; fall back to the HTTP status)
one of"INVALID_REQUEST","UNAUTHORIZED","MISSING_AUTHENTICATION","FORBIDDEN","REQUEST_REJECTED","NOT_FOUND","ROUTE_NOT_FOUND","METHOD_NOT_ALLOWED","UPLOAD_INCOMPLETE","ALREADY_COMPLETED","ORIGINAL_UNAVAILABLE","EDIT_SOURCE_NOT_READY","NOT_AN_UPLOAD","IDEMPOTENCY_KEY_REUSED","IDEMPOTENCY_IN_PROGRESS","INVALID_STATE","TOO_LARGE","RATE_LIMITED","QUOTA_EXCEEDED","DAILY_LIMIT","VIDEOS_PAUSED","PLAN_UPGRADE_REQUIRED","PLAN_LIMIT_REACHED","INVALID_CODE","TERMS_REQUIRED","SESSION_REQUIRED","ACCOUNT_DISABLED","EMAIL_NOT_CONFIGURED","EMAIL_FAILED","UPSTREAM_ERROR","UNAVAILABLE"requestIdstringrequiredQuote this when reporting a problem
detailsarray of objectValidation errors only (400 INVALID_REQUEST) - every problem found, with its field path
fieldstringmessagestring
operationId getWebhookConfig
PUT/webhookSet or update your webhook endpoint
Request body required
{
"url": "string",
"events": [
"video.ready"
],
"enabled": false
}Schema
urlstring (uri)requiredHTTPS only, public addresses only
eventsarray of stringDefaults to all events if omitted
enabledbooleanDefaults to true
Responses
200Saved
{
"configured": false,
"url": "string",
"events": [
"string"
],
"enabled": false,
"secret": "string",
"createdAt": "string",
"updatedAt": "string"
}Schema
configuredbooleanurlstringeventsarray of stringenabledbooleansecretstringcreatedAtstring (date-time)updatedAtstring (date-time)
400INVALID_REQUEST: invalid body or parameter (the message names the field)
{
"message": "Daily limit reached (50 videos per 24 hours); try again later",
"error": "DAILY_LIMIT",
"requestId": "string",
"details": [
{
"field": "edit.clips[0].end",
"message": "80s is past the end of video …, which is 60.0s (1:00); use null for \"to the end\""
}
]
}Schema
messagestringrequiredHuman-readable; wording may change
errorstringrequiredStable machine-readable code (new codes may be added; fall back to the HTTP status)
one of"INVALID_REQUEST","UNAUTHORIZED","MISSING_AUTHENTICATION","FORBIDDEN","REQUEST_REJECTED","NOT_FOUND","ROUTE_NOT_FOUND","METHOD_NOT_ALLOWED","UPLOAD_INCOMPLETE","ALREADY_COMPLETED","ORIGINAL_UNAVAILABLE","EDIT_SOURCE_NOT_READY","NOT_AN_UPLOAD","IDEMPOTENCY_KEY_REUSED","IDEMPOTENCY_IN_PROGRESS","INVALID_STATE","TOO_LARGE","RATE_LIMITED","QUOTA_EXCEEDED","DAILY_LIMIT","VIDEOS_PAUSED","PLAN_UPGRADE_REQUIRED","PLAN_LIMIT_REACHED","INVALID_CODE","TERMS_REQUIRED","SESSION_REQUIRED","ACCOUNT_DISABLED","EMAIL_NOT_CONFIGURED","EMAIL_FAILED","UPSTREAM_ERROR","UNAVAILABLE"requestIdstringrequiredQuote this when reporting a problem
detailsarray of objectValidation errors only (400 INVALID_REQUEST) - every problem found, with its field path
fieldstringmessagestring
401UNAUTHORIZED: missing, unknown or revoked API key
{
"message": "Daily limit reached (50 videos per 24 hours); try again later",
"error": "DAILY_LIMIT",
"requestId": "string",
"details": [
{
"field": "edit.clips[0].end",
"message": "80s is past the end of video …, which is 60.0s (1:00); use null for \"to the end\""
}
]
}Schema
messagestringrequiredHuman-readable; wording may change
errorstringrequiredStable machine-readable code (new codes may be added; fall back to the HTTP status)
one of"INVALID_REQUEST","UNAUTHORIZED","MISSING_AUTHENTICATION","FORBIDDEN","REQUEST_REJECTED","NOT_FOUND","ROUTE_NOT_FOUND","METHOD_NOT_ALLOWED","UPLOAD_INCOMPLETE","ALREADY_COMPLETED","ORIGINAL_UNAVAILABLE","EDIT_SOURCE_NOT_READY","NOT_AN_UPLOAD","IDEMPOTENCY_KEY_REUSED","IDEMPOTENCY_IN_PROGRESS","INVALID_STATE","TOO_LARGE","RATE_LIMITED","QUOTA_EXCEEDED","DAILY_LIMIT","VIDEOS_PAUSED","PLAN_UPGRADE_REQUIRED","PLAN_LIMIT_REACHED","INVALID_CODE","TERMS_REQUIRED","SESSION_REQUIRED","ACCOUNT_DISABLED","EMAIL_NOT_CONFIGURED","EMAIL_FAILED","UPSTREAM_ERROR","UNAVAILABLE"requestIdstringrequiredQuote this when reporting a problem
detailsarray of objectValidation errors only (400 INVALID_REQUEST) - every problem found, with its field path
fieldstringmessagestring
429RATE_LIMITED (retry with backoff), QUOTA_EXCEEDED, DAILY_LIMIT or VIDEOS_PAUSED
{
"message": "Daily limit reached (50 videos per 24 hours); try again later",
"error": "DAILY_LIMIT",
"requestId": "string",
"details": [
{
"field": "edit.clips[0].end",
"message": "80s is past the end of video …, which is 60.0s (1:00); use null for \"to the end\""
}
]
}Schema
messagestringrequiredHuman-readable; wording may change
errorstringrequiredStable machine-readable code (new codes may be added; fall back to the HTTP status)
one of"INVALID_REQUEST","UNAUTHORIZED","MISSING_AUTHENTICATION","FORBIDDEN","REQUEST_REJECTED","NOT_FOUND","ROUTE_NOT_FOUND","METHOD_NOT_ALLOWED","UPLOAD_INCOMPLETE","ALREADY_COMPLETED","ORIGINAL_UNAVAILABLE","EDIT_SOURCE_NOT_READY","NOT_AN_UPLOAD","IDEMPOTENCY_KEY_REUSED","IDEMPOTENCY_IN_PROGRESS","INVALID_STATE","TOO_LARGE","RATE_LIMITED","QUOTA_EXCEEDED","DAILY_LIMIT","VIDEOS_PAUSED","PLAN_UPGRADE_REQUIRED","PLAN_LIMIT_REACHED","INVALID_CODE","TERMS_REQUIRED","SESSION_REQUIRED","ACCOUNT_DISABLED","EMAIL_NOT_CONFIGURED","EMAIL_FAILED","UPSTREAM_ERROR","UNAVAILABLE"requestIdstringrequiredQuote this when reporting a problem
detailsarray of objectValidation errors only (400 INVALID_REQUEST) - every problem found, with its field path
fieldstringmessagestring
503UNAVAILABLE: temporary failure; retry with backoff
{
"message": "Daily limit reached (50 videos per 24 hours); try again later",
"error": "DAILY_LIMIT",
"requestId": "string",
"details": [
{
"field": "edit.clips[0].end",
"message": "80s is past the end of video …, which is 60.0s (1:00); use null for \"to the end\""
}
]
}Schema
messagestringrequiredHuman-readable; wording may change
errorstringrequiredStable machine-readable code (new codes may be added; fall back to the HTTP status)
one of"INVALID_REQUEST","UNAUTHORIZED","MISSING_AUTHENTICATION","FORBIDDEN","REQUEST_REJECTED","NOT_FOUND","ROUTE_NOT_FOUND","METHOD_NOT_ALLOWED","UPLOAD_INCOMPLETE","ALREADY_COMPLETED","ORIGINAL_UNAVAILABLE","EDIT_SOURCE_NOT_READY","NOT_AN_UPLOAD","IDEMPOTENCY_KEY_REUSED","IDEMPOTENCY_IN_PROGRESS","INVALID_STATE","TOO_LARGE","RATE_LIMITED","QUOTA_EXCEEDED","DAILY_LIMIT","VIDEOS_PAUSED","PLAN_UPGRADE_REQUIRED","PLAN_LIMIT_REACHED","INVALID_CODE","TERMS_REQUIRED","SESSION_REQUIRED","ACCOUNT_DISABLED","EMAIL_NOT_CONFIGURED","EMAIL_FAILED","UPSTREAM_ERROR","UNAVAILABLE"requestIdstringrequiredQuote this when reporting a problem
detailsarray of objectValidation errors only (400 INVALID_REQUEST) - every problem found, with its field path
fieldstringmessagestring
operationId setWebhookConfig
DELETE/webhookRemove your webhook endpoint
Responses
200Deleted
{
"configured": false
}Schema
configuredboolean
401UNAUTHORIZED: missing, unknown or revoked API key
{
"message": "Daily limit reached (50 videos per 24 hours); try again later",
"error": "DAILY_LIMIT",
"requestId": "string",
"details": [
{
"field": "edit.clips[0].end",
"message": "80s is past the end of video …, which is 60.0s (1:00); use null for \"to the end\""
}
]
}Schema
messagestringrequiredHuman-readable; wording may change
errorstringrequiredStable machine-readable code (new codes may be added; fall back to the HTTP status)
one of"INVALID_REQUEST","UNAUTHORIZED","MISSING_AUTHENTICATION","FORBIDDEN","REQUEST_REJECTED","NOT_FOUND","ROUTE_NOT_FOUND","METHOD_NOT_ALLOWED","UPLOAD_INCOMPLETE","ALREADY_COMPLETED","ORIGINAL_UNAVAILABLE","EDIT_SOURCE_NOT_READY","NOT_AN_UPLOAD","IDEMPOTENCY_KEY_REUSED","IDEMPOTENCY_IN_PROGRESS","INVALID_STATE","TOO_LARGE","RATE_LIMITED","QUOTA_EXCEEDED","DAILY_LIMIT","VIDEOS_PAUSED","PLAN_UPGRADE_REQUIRED","PLAN_LIMIT_REACHED","INVALID_CODE","TERMS_REQUIRED","SESSION_REQUIRED","ACCOUNT_DISABLED","EMAIL_NOT_CONFIGURED","EMAIL_FAILED","UPSTREAM_ERROR","UNAVAILABLE"requestIdstringrequiredQuote this when reporting a problem
detailsarray of objectValidation errors only (400 INVALID_REQUEST) - every problem found, with its field path
fieldstringmessagestring
429RATE_LIMITED (retry with backoff), QUOTA_EXCEEDED, DAILY_LIMIT or VIDEOS_PAUSED
{
"message": "Daily limit reached (50 videos per 24 hours); try again later",
"error": "DAILY_LIMIT",
"requestId": "string",
"details": [
{
"field": "edit.clips[0].end",
"message": "80s is past the end of video …, which is 60.0s (1:00); use null for \"to the end\""
}
]
}Schema
messagestringrequiredHuman-readable; wording may change
errorstringrequiredStable machine-readable code (new codes may be added; fall back to the HTTP status)
one of"INVALID_REQUEST","UNAUTHORIZED","MISSING_AUTHENTICATION","FORBIDDEN","REQUEST_REJECTED","NOT_FOUND","ROUTE_NOT_FOUND","METHOD_NOT_ALLOWED","UPLOAD_INCOMPLETE","ALREADY_COMPLETED","ORIGINAL_UNAVAILABLE","EDIT_SOURCE_NOT_READY","NOT_AN_UPLOAD","IDEMPOTENCY_KEY_REUSED","IDEMPOTENCY_IN_PROGRESS","INVALID_STATE","TOO_LARGE","RATE_LIMITED","QUOTA_EXCEEDED","DAILY_LIMIT","VIDEOS_PAUSED","PLAN_UPGRADE_REQUIRED","PLAN_LIMIT_REACHED","INVALID_CODE","TERMS_REQUIRED","SESSION_REQUIRED","ACCOUNT_DISABLED","EMAIL_NOT_CONFIGURED","EMAIL_FAILED","UPSTREAM_ERROR","UNAVAILABLE"requestIdstringrequiredQuote this when reporting a problem
detailsarray of objectValidation errors only (400 INVALID_REQUEST) - every problem found, with its field path
fieldstringmessagestring
503UNAVAILABLE: temporary failure; retry with backoff
{
"message": "Daily limit reached (50 videos per 24 hours); try again later",
"error": "DAILY_LIMIT",
"requestId": "string",
"details": [
{
"field": "edit.clips[0].end",
"message": "80s is past the end of video …, which is 60.0s (1:00); use null for \"to the end\""
}
]
}Schema
messagestringrequiredHuman-readable; wording may change
errorstringrequiredStable machine-readable code (new codes may be added; fall back to the HTTP status)
one of"INVALID_REQUEST","UNAUTHORIZED","MISSING_AUTHENTICATION","FORBIDDEN","REQUEST_REJECTED","NOT_FOUND","ROUTE_NOT_FOUND","METHOD_NOT_ALLOWED","UPLOAD_INCOMPLETE","ALREADY_COMPLETED","ORIGINAL_UNAVAILABLE","EDIT_SOURCE_NOT_READY","NOT_AN_UPLOAD","IDEMPOTENCY_KEY_REUSED","IDEMPOTENCY_IN_PROGRESS","INVALID_STATE","TOO_LARGE","RATE_LIMITED","QUOTA_EXCEEDED","DAILY_LIMIT","VIDEOS_PAUSED","PLAN_UPGRADE_REQUIRED","PLAN_LIMIT_REACHED","INVALID_CODE","TERMS_REQUIRED","SESSION_REQUIRED","ACCOUNT_DISABLED","EMAIL_NOT_CONFIGURED","EMAIL_FAILED","UPSTREAM_ERROR","UNAVAILABLE"requestIdstringrequiredQuote this when reporting a problem
detailsarray of objectValidation errors only (400 INVALID_REQUEST) - every problem found, with its field path
fieldstringmessagestring
operationId deleteWebhookConfig
POST/webhook/rotateGenerate a new signing secret (keep the URL)
Responses
200New secret
{
"configured": false,
"url": "string",
"events": [
"string"
],
"enabled": false,
"secret": "string",
"createdAt": "string",
"updatedAt": "string"
}Schema
configuredbooleanurlstringeventsarray of stringenabledbooleansecretstringNew whsec_-prefixed secret; old one stops working immediately
createdAtstring (date-time)updatedAtstring (date-time)
401UNAUTHORIZED: missing, unknown or revoked API key
{
"message": "Daily limit reached (50 videos per 24 hours); try again later",
"error": "DAILY_LIMIT",
"requestId": "string",
"details": [
{
"field": "edit.clips[0].end",
"message": "80s is past the end of video …, which is 60.0s (1:00); use null for \"to the end\""
}
]
}Schema
messagestringrequiredHuman-readable; wording may change
errorstringrequiredStable machine-readable code (new codes may be added; fall back to the HTTP status)
one of"INVALID_REQUEST","UNAUTHORIZED","MISSING_AUTHENTICATION","FORBIDDEN","REQUEST_REJECTED","NOT_FOUND","ROUTE_NOT_FOUND","METHOD_NOT_ALLOWED","UPLOAD_INCOMPLETE","ALREADY_COMPLETED","ORIGINAL_UNAVAILABLE","EDIT_SOURCE_NOT_READY","NOT_AN_UPLOAD","IDEMPOTENCY_KEY_REUSED","IDEMPOTENCY_IN_PROGRESS","INVALID_STATE","TOO_LARGE","RATE_LIMITED","QUOTA_EXCEEDED","DAILY_LIMIT","VIDEOS_PAUSED","PLAN_UPGRADE_REQUIRED","PLAN_LIMIT_REACHED","INVALID_CODE","TERMS_REQUIRED","SESSION_REQUIRED","ACCOUNT_DISABLED","EMAIL_NOT_CONFIGURED","EMAIL_FAILED","UPSTREAM_ERROR","UNAVAILABLE"requestIdstringrequiredQuote this when reporting a problem
detailsarray of objectValidation errors only (400 INVALID_REQUEST) - every problem found, with its field path
fieldstringmessagestring
404No webhook is set up yet
{
"message": "Daily limit reached (50 videos per 24 hours); try again later",
"error": "DAILY_LIMIT",
"requestId": "string",
"details": [
{
"field": "edit.clips[0].end",
"message": "80s is past the end of video …, which is 60.0s (1:00); use null for \"to the end\""
}
]
}Schema
messagestringrequiredHuman-readable; wording may change
errorstringrequiredStable machine-readable code (new codes may be added; fall back to the HTTP status)
one of"INVALID_REQUEST","UNAUTHORIZED","MISSING_AUTHENTICATION","FORBIDDEN","REQUEST_REJECTED","NOT_FOUND","ROUTE_NOT_FOUND","METHOD_NOT_ALLOWED","UPLOAD_INCOMPLETE","ALREADY_COMPLETED","ORIGINAL_UNAVAILABLE","EDIT_SOURCE_NOT_READY","NOT_AN_UPLOAD","IDEMPOTENCY_KEY_REUSED","IDEMPOTENCY_IN_PROGRESS","INVALID_STATE","TOO_LARGE","RATE_LIMITED","QUOTA_EXCEEDED","DAILY_LIMIT","VIDEOS_PAUSED","PLAN_UPGRADE_REQUIRED","PLAN_LIMIT_REACHED","INVALID_CODE","TERMS_REQUIRED","SESSION_REQUIRED","ACCOUNT_DISABLED","EMAIL_NOT_CONFIGURED","EMAIL_FAILED","UPSTREAM_ERROR","UNAVAILABLE"requestIdstringrequiredQuote this when reporting a problem
detailsarray of objectValidation errors only (400 INVALID_REQUEST) - every problem found, with its field path
fieldstringmessagestring
429RATE_LIMITED (retry with backoff), QUOTA_EXCEEDED, DAILY_LIMIT or VIDEOS_PAUSED
{
"message": "Daily limit reached (50 videos per 24 hours); try again later",
"error": "DAILY_LIMIT",
"requestId": "string",
"details": [
{
"field": "edit.clips[0].end",
"message": "80s is past the end of video …, which is 60.0s (1:00); use null for \"to the end\""
}
]
}Schema
messagestringrequiredHuman-readable; wording may change
errorstringrequiredStable machine-readable code (new codes may be added; fall back to the HTTP status)
one of"INVALID_REQUEST","UNAUTHORIZED","MISSING_AUTHENTICATION","FORBIDDEN","REQUEST_REJECTED","NOT_FOUND","ROUTE_NOT_FOUND","METHOD_NOT_ALLOWED","UPLOAD_INCOMPLETE","ALREADY_COMPLETED","ORIGINAL_UNAVAILABLE","EDIT_SOURCE_NOT_READY","NOT_AN_UPLOAD","IDEMPOTENCY_KEY_REUSED","IDEMPOTENCY_IN_PROGRESS","INVALID_STATE","TOO_LARGE","RATE_LIMITED","QUOTA_EXCEEDED","DAILY_LIMIT","VIDEOS_PAUSED","PLAN_UPGRADE_REQUIRED","PLAN_LIMIT_REACHED","INVALID_CODE","TERMS_REQUIRED","SESSION_REQUIRED","ACCOUNT_DISABLED","EMAIL_NOT_CONFIGURED","EMAIL_FAILED","UPSTREAM_ERROR","UNAVAILABLE"requestIdstringrequiredQuote this when reporting a problem
detailsarray of objectValidation errors only (400 INVALID_REQUEST) - every problem found, with its field path
fieldstringmessagestring
503UNAVAILABLE: temporary failure; retry with backoff
{
"message": "Daily limit reached (50 videos per 24 hours); try again later",
"error": "DAILY_LIMIT",
"requestId": "string",
"details": [
{
"field": "edit.clips[0].end",
"message": "80s is past the end of video …, which is 60.0s (1:00); use null for \"to the end\""
}
]
}Schema
messagestringrequiredHuman-readable; wording may change
errorstringrequiredStable machine-readable code (new codes may be added; fall back to the HTTP status)
one of"INVALID_REQUEST","UNAUTHORIZED","MISSING_AUTHENTICATION","FORBIDDEN","REQUEST_REJECTED","NOT_FOUND","ROUTE_NOT_FOUND","METHOD_NOT_ALLOWED","UPLOAD_INCOMPLETE","ALREADY_COMPLETED","ORIGINAL_UNAVAILABLE","EDIT_SOURCE_NOT_READY","NOT_AN_UPLOAD","IDEMPOTENCY_KEY_REUSED","IDEMPOTENCY_IN_PROGRESS","INVALID_STATE","TOO_LARGE","RATE_LIMITED","QUOTA_EXCEEDED","DAILY_LIMIT","VIDEOS_PAUSED","PLAN_UPGRADE_REQUIRED","PLAN_LIMIT_REACHED","INVALID_CODE","TERMS_REQUIRED","SESSION_REQUIRED","ACCOUNT_DISABLED","EMAIL_NOT_CONFIGURED","EMAIL_FAILED","UPSTREAM_ERROR","UNAVAILABLE"requestIdstringrequiredQuote this when reporting a problem
detailsarray of objectValidation errors only (400 INVALID_REQUEST) - every problem found, with its field path
fieldstringmessagestring
operationId rotateWebhookSecret
POST/webhook/testSend a test event and report the result immediately
Responses
200Test sent and one attempt reported
{
"ok": false,
"status": 0,
"ms": 0,
"error": "string",
"response": "string",
"eventId": "string"
}Schema
okbooleanstatusintegerHTTP status code from the endpoint
msintegerMilliseconds to respond (or error)
errorstringError message if not ok
responsestringFirst 500 bytes of the response body
eventIdstringThe test event ID
401UNAUTHORIZED: missing, unknown or revoked API key
{
"message": "Daily limit reached (50 videos per 24 hours); try again later",
"error": "DAILY_LIMIT",
"requestId": "string",
"details": [
{
"field": "edit.clips[0].end",
"message": "80s is past the end of video …, which is 60.0s (1:00); use null for \"to the end\""
}
]
}Schema
messagestringrequiredHuman-readable; wording may change
errorstringrequiredStable machine-readable code (new codes may be added; fall back to the HTTP status)
one of"INVALID_REQUEST","UNAUTHORIZED","MISSING_AUTHENTICATION","FORBIDDEN","REQUEST_REJECTED","NOT_FOUND","ROUTE_NOT_FOUND","METHOD_NOT_ALLOWED","UPLOAD_INCOMPLETE","ALREADY_COMPLETED","ORIGINAL_UNAVAILABLE","EDIT_SOURCE_NOT_READY","NOT_AN_UPLOAD","IDEMPOTENCY_KEY_REUSED","IDEMPOTENCY_IN_PROGRESS","INVALID_STATE","TOO_LARGE","RATE_LIMITED","QUOTA_EXCEEDED","DAILY_LIMIT","VIDEOS_PAUSED","PLAN_UPGRADE_REQUIRED","PLAN_LIMIT_REACHED","INVALID_CODE","TERMS_REQUIRED","SESSION_REQUIRED","ACCOUNT_DISABLED","EMAIL_NOT_CONFIGURED","EMAIL_FAILED","UPSTREAM_ERROR","UNAVAILABLE"requestIdstringrequiredQuote this when reporting a problem
detailsarray of objectValidation errors only (400 INVALID_REQUEST) - every problem found, with its field path
fieldstringmessagestring
404No webhook is set up yet
{
"message": "Daily limit reached (50 videos per 24 hours); try again later",
"error": "DAILY_LIMIT",
"requestId": "string",
"details": [
{
"field": "edit.clips[0].end",
"message": "80s is past the end of video …, which is 60.0s (1:00); use null for \"to the end\""
}
]
}Schema
messagestringrequiredHuman-readable; wording may change
errorstringrequiredStable machine-readable code (new codes may be added; fall back to the HTTP status)
one of"INVALID_REQUEST","UNAUTHORIZED","MISSING_AUTHENTICATION","FORBIDDEN","REQUEST_REJECTED","NOT_FOUND","ROUTE_NOT_FOUND","METHOD_NOT_ALLOWED","UPLOAD_INCOMPLETE","ALREADY_COMPLETED","ORIGINAL_UNAVAILABLE","EDIT_SOURCE_NOT_READY","NOT_AN_UPLOAD","IDEMPOTENCY_KEY_REUSED","IDEMPOTENCY_IN_PROGRESS","INVALID_STATE","TOO_LARGE","RATE_LIMITED","QUOTA_EXCEEDED","DAILY_LIMIT","VIDEOS_PAUSED","PLAN_UPGRADE_REQUIRED","PLAN_LIMIT_REACHED","INVALID_CODE","TERMS_REQUIRED","SESSION_REQUIRED","ACCOUNT_DISABLED","EMAIL_NOT_CONFIGURED","EMAIL_FAILED","UPSTREAM_ERROR","UNAVAILABLE"requestIdstringrequiredQuote this when reporting a problem
detailsarray of objectValidation errors only (400 INVALID_REQUEST) - every problem found, with its field path
fieldstringmessagestring
429RATE_LIMITED (retry with backoff), QUOTA_EXCEEDED, DAILY_LIMIT or VIDEOS_PAUSED
{
"message": "Daily limit reached (50 videos per 24 hours); try again later",
"error": "DAILY_LIMIT",
"requestId": "string",
"details": [
{
"field": "edit.clips[0].end",
"message": "80s is past the end of video …, which is 60.0s (1:00); use null for \"to the end\""
}
]
}Schema
messagestringrequiredHuman-readable; wording may change
errorstringrequiredStable machine-readable code (new codes may be added; fall back to the HTTP status)
one of"INVALID_REQUEST","UNAUTHORIZED","MISSING_AUTHENTICATION","FORBIDDEN","REQUEST_REJECTED","NOT_FOUND","ROUTE_NOT_FOUND","METHOD_NOT_ALLOWED","UPLOAD_INCOMPLETE","ALREADY_COMPLETED","ORIGINAL_UNAVAILABLE","EDIT_SOURCE_NOT_READY","NOT_AN_UPLOAD","IDEMPOTENCY_KEY_REUSED","IDEMPOTENCY_IN_PROGRESS","INVALID_STATE","TOO_LARGE","RATE_LIMITED","QUOTA_EXCEEDED","DAILY_LIMIT","VIDEOS_PAUSED","PLAN_UPGRADE_REQUIRED","PLAN_LIMIT_REACHED","INVALID_CODE","TERMS_REQUIRED","SESSION_REQUIRED","ACCOUNT_DISABLED","EMAIL_NOT_CONFIGURED","EMAIL_FAILED","UPSTREAM_ERROR","UNAVAILABLE"requestIdstringrequiredQuote this when reporting a problem
detailsarray of objectValidation errors only (400 INVALID_REQUEST) - every problem found, with its field path
fieldstringmessagestring
503UNAVAILABLE: temporary failure; retry with backoff
{
"message": "Daily limit reached (50 videos per 24 hours); try again later",
"error": "DAILY_LIMIT",
"requestId": "string",
"details": [
{
"field": "edit.clips[0].end",
"message": "80s is past the end of video …, which is 60.0s (1:00); use null for \"to the end\""
}
]
}Schema
messagestringrequiredHuman-readable; wording may change
errorstringrequiredStable machine-readable code (new codes may be added; fall back to the HTTP status)
one of"INVALID_REQUEST","UNAUTHORIZED","MISSING_AUTHENTICATION","FORBIDDEN","REQUEST_REJECTED","NOT_FOUND","ROUTE_NOT_FOUND","METHOD_NOT_ALLOWED","UPLOAD_INCOMPLETE","ALREADY_COMPLETED","ORIGINAL_UNAVAILABLE","EDIT_SOURCE_NOT_READY","NOT_AN_UPLOAD","IDEMPOTENCY_KEY_REUSED","IDEMPOTENCY_IN_PROGRESS","INVALID_STATE","TOO_LARGE","RATE_LIMITED","QUOTA_EXCEEDED","DAILY_LIMIT","VIDEOS_PAUSED","PLAN_UPGRADE_REQUIRED","PLAN_LIMIT_REACHED","INVALID_CODE","TERMS_REQUIRED","SESSION_REQUIRED","ACCOUNT_DISABLED","EMAIL_NOT_CONFIGURED","EMAIL_FAILED","UPSTREAM_ERROR","UNAVAILABLE"requestIdstringrequiredQuote this when reporting a problem
detailsarray of objectValidation errors only (400 INVALID_REQUEST) - every problem found, with its field path
fieldstringmessagestring
operationId testWebhook
GET/webhook/deliveriesRecent webhook delivery attempts (kept for 30 days)
Parameters
| Name | In | Type | Description |
|---|---|---|---|
limit | query | integer |
Responses
200Delivery log
{
"deliveries": [
{
"eventId": "string",
"type": "string",
"attempt": 0,
"ok": false,
"status": 0,
"ms": 0,
"error": "string",
"response": "string",
"at": 0,
"final": false
}
]
}Schema
deliveriesarray of objecteventIdstringtypestringattemptintegerokbooleanstatusintegermsintegerMilliseconds taken
errorstringresponsestringFirst 500 bytes of response
atintegerUnix time
finalbooleanWhether retries will stop
401UNAUTHORIZED: missing, unknown or revoked API key
{
"message": "Daily limit reached (50 videos per 24 hours); try again later",
"error": "DAILY_LIMIT",
"requestId": "string",
"details": [
{
"field": "edit.clips[0].end",
"message": "80s is past the end of video …, which is 60.0s (1:00); use null for \"to the end\""
}
]
}Schema
messagestringrequiredHuman-readable; wording may change
errorstringrequiredStable machine-readable code (new codes may be added; fall back to the HTTP status)
one of"INVALID_REQUEST","UNAUTHORIZED","MISSING_AUTHENTICATION","FORBIDDEN","REQUEST_REJECTED","NOT_FOUND","ROUTE_NOT_FOUND","METHOD_NOT_ALLOWED","UPLOAD_INCOMPLETE","ALREADY_COMPLETED","ORIGINAL_UNAVAILABLE","EDIT_SOURCE_NOT_READY","NOT_AN_UPLOAD","IDEMPOTENCY_KEY_REUSED","IDEMPOTENCY_IN_PROGRESS","INVALID_STATE","TOO_LARGE","RATE_LIMITED","QUOTA_EXCEEDED","DAILY_LIMIT","VIDEOS_PAUSED","PLAN_UPGRADE_REQUIRED","PLAN_LIMIT_REACHED","INVALID_CODE","TERMS_REQUIRED","SESSION_REQUIRED","ACCOUNT_DISABLED","EMAIL_NOT_CONFIGURED","EMAIL_FAILED","UPSTREAM_ERROR","UNAVAILABLE"requestIdstringrequiredQuote this when reporting a problem
detailsarray of objectValidation errors only (400 INVALID_REQUEST) - every problem found, with its field path
fieldstringmessagestring
429RATE_LIMITED (retry with backoff), QUOTA_EXCEEDED, DAILY_LIMIT or VIDEOS_PAUSED
{
"message": "Daily limit reached (50 videos per 24 hours); try again later",
"error": "DAILY_LIMIT",
"requestId": "string",
"details": [
{
"field": "edit.clips[0].end",
"message": "80s is past the end of video …, which is 60.0s (1:00); use null for \"to the end\""
}
]
}Schema
messagestringrequiredHuman-readable; wording may change
errorstringrequiredStable machine-readable code (new codes may be added; fall back to the HTTP status)
one of"INVALID_REQUEST","UNAUTHORIZED","MISSING_AUTHENTICATION","FORBIDDEN","REQUEST_REJECTED","NOT_FOUND","ROUTE_NOT_FOUND","METHOD_NOT_ALLOWED","UPLOAD_INCOMPLETE","ALREADY_COMPLETED","ORIGINAL_UNAVAILABLE","EDIT_SOURCE_NOT_READY","NOT_AN_UPLOAD","IDEMPOTENCY_KEY_REUSED","IDEMPOTENCY_IN_PROGRESS","INVALID_STATE","TOO_LARGE","RATE_LIMITED","QUOTA_EXCEEDED","DAILY_LIMIT","VIDEOS_PAUSED","PLAN_UPGRADE_REQUIRED","PLAN_LIMIT_REACHED","INVALID_CODE","TERMS_REQUIRED","SESSION_REQUIRED","ACCOUNT_DISABLED","EMAIL_NOT_CONFIGURED","EMAIL_FAILED","UPSTREAM_ERROR","UNAVAILABLE"requestIdstringrequiredQuote this when reporting a problem
detailsarray of objectValidation errors only (400 INVALID_REQUEST) - every problem found, with its field path
fieldstringmessagestring
503UNAVAILABLE: temporary failure; retry with backoff
{
"message": "Daily limit reached (50 videos per 24 hours); try again later",
"error": "DAILY_LIMIT",
"requestId": "string",
"details": [
{
"field": "edit.clips[0].end",
"message": "80s is past the end of video …, which is 60.0s (1:00); use null for \"to the end\""
}
]
}Schema
messagestringrequiredHuman-readable; wording may change
errorstringrequiredStable machine-readable code (new codes may be added; fall back to the HTTP status)
one of"INVALID_REQUEST","UNAUTHORIZED","MISSING_AUTHENTICATION","FORBIDDEN","REQUEST_REJECTED","NOT_FOUND","ROUTE_NOT_FOUND","METHOD_NOT_ALLOWED","UPLOAD_INCOMPLETE","ALREADY_COMPLETED","ORIGINAL_UNAVAILABLE","EDIT_SOURCE_NOT_READY","NOT_AN_UPLOAD","IDEMPOTENCY_KEY_REUSED","IDEMPOTENCY_IN_PROGRESS","INVALID_STATE","TOO_LARGE","RATE_LIMITED","QUOTA_EXCEEDED","DAILY_LIMIT","VIDEOS_PAUSED","PLAN_UPGRADE_REQUIRED","PLAN_LIMIT_REACHED","INVALID_CODE","TERMS_REQUIRED","SESSION_REQUIRED","ACCOUNT_DISABLED","EMAIL_NOT_CONFIGURED","EMAIL_FAILED","UPSTREAM_ERROR","UNAVAILABLE"requestIdstringrequiredQuote this when reporting a problem
detailsarray of objectValidation errors only (400 INVALID_REQUEST) - every problem found, with its field path
fieldstringmessagestring
operationId listWebhookDeliveries
Schemas
Errorobject
Every error response (from the API and from API Gateway). Program against error; show message.
messagestringrequiredHuman-readable; wording may change
errorstringrequiredStable machine-readable code (new codes may be added; fall back to the HTTP status)
one of"INVALID_REQUEST","UNAUTHORIZED","MISSING_AUTHENTICATION","FORBIDDEN","REQUEST_REJECTED","NOT_FOUND","ROUTE_NOT_FOUND","METHOD_NOT_ALLOWED","UPLOAD_INCOMPLETE","ALREADY_COMPLETED","ORIGINAL_UNAVAILABLE","EDIT_SOURCE_NOT_READY","NOT_AN_UPLOAD","IDEMPOTENCY_KEY_REUSED","IDEMPOTENCY_IN_PROGRESS","INVALID_STATE","TOO_LARGE","RATE_LIMITED","QUOTA_EXCEEDED","DAILY_LIMIT","VIDEOS_PAUSED","PLAN_UPGRADE_REQUIRED","PLAN_LIMIT_REACHED","INVALID_CODE","TERMS_REQUIRED","SESSION_REQUIRED","ACCOUNT_DISABLED","EMAIL_NOT_CONFIGURED","EMAIL_FAILED","UPSTREAM_ERROR","UNAVAILABLE"requestIdstringrequiredQuote this when reporting a problem
detailsarray of objectValidation errors only (400 INVALID_REQUEST) - every problem found, with its field path
fieldstringmessagestring
{
"message": "Daily limit reached (50 videos per 24 hours); try again later",
"error": "DAILY_LIMIT",
"requestId": "string",
"details": [
{
"field": "edit.clips[0].end",
"message": "80s is past the end of video …, which is 60.0s (1:00); use null for \"to the end\""
}
]
}Metadataobject
Customer key-value pairs (up to 20 keys, 4 KB total). Keys are 1-40 [A-Za-z0-9_.-]; values are strings up to 500 chars.
{}UploadRequestobject
fileNamestringrequiredsizeBytesintegerRequired unless streaming
maxHeightintegerHighest output quality; never upscales
one of720,1080,1440,2160· default1080cameraobjectOptional second file shown picture-in-picture over the first (screen + camera)
fileNamestringsizeBytesinteger
layoutstringOptional with camera
one of"pip"streamingbooleanSize unknown yet (e.g. still recording); parts are signed per size
prestartbooleanWith streaming
metadataMetadataCustomer key-value pairs (up to 20 keys, 4 KB total). Keys are 1-40 [A-Za-z0-9_.-]; values are strings up to 500 chars.
{
"fileName": "lesson1.mp4",
"sizeBytes": 104857600,
"maxHeight": 1080,
"camera": {
"fileName": "string",
"sizeBytes": 0
},
"layout": "pip",
"streaming": false,
"prestart": false,
"metadata": {}
}ImportRequestobject
Import a video from a public HTTPS URL instead of uploading. The URL is downloaded by the API.
sourceUrlstring (uri)requiredHTTPS URL (public internet addresses only; no username/password) with the video file. At most 3 redirects, each checked. The server must answer 200; file size is checked against the upload limit.
max length2048fileNamestringDisplay name (default: last URL path segment). 1–200 characters.
maxHeightintegerHighest output quality; never upscales
one of720,1080,1440,2160· default1080trimTrimKeep only part of the upload (frame-accurate, no extra encode). Redaction times are on the trimmed timeline.
startnumbermin0· default0endnumbernull = to the end
redactionsarray of RedactionRegions to blur or black out
max items20typestringrequiredbox = solid black bar
one of"blur","box"shapestringone of"rect","ellipse"· default"rect"xnumberFraction of the final frame width
min0· max1ynumbermin0· max1wnumbermin0· max1hnumbermin0· max1startnumberSeconds
default0endnumberSeconds; null = until the end
trackarray of objectKeyframes for moving content, in time order; the region glides between them (instead of x, y, w, h)
max items50timenumberrequiredSeconds on the output timeline
min0xnumberrequiredmin0· max1ynumberrequiredmin0· max1wnumberrequiredmin0· max1hnumberrequiredmin0· max1
deleteOriginalbooleanDelete the imported file once the output is published
metadataMetadataCustomer key-value pairs (up to 20 keys, 4 KB total). Keys are 1-40 [A-Za-z0-9_.-]; values are strings up to 500 chars.
{
"sourceUrl": "string",
"fileName": "string",
"maxHeight": 1080,
"trim": {
"start": 0,
"end": 0
},
"redactions": [
{
"type": "blur",
"shape": "rect",
"x": 0,
"y": 0,
"w": 0,
"h": 0,
"start": 0,
"end": 0,
"track": [
{
"time": 0,
"x": 0,
"y": 0,
"w": 0,
"h": 0
}
]
}
],
"deleteOriginal": false,
"metadata": {}
}EditRequestobject
A new video from clips of your finished videos, played in order (trim, stitch, splice).
editobjectrequired- max items
50videoIdstringrequiredOne of your finished (SUCCEEDED) videos; may repeat
pattern"^[a-f0-9]{32}$"startnumberSeconds into that video
min0· default0endnumberSeconds into that video; null = to its end
transitionobjectHow this clip starts from the previous one (hard cut if omitted; not on the first clip)
typestringone of"fade","fadeblack"· default"fade"secondsnumbermin0.1· max3· default0.5
fileNamestringDisplay name (default: Edit of N clips)
maxHeightintegerone of720,1080,1440,2160· default1080redactionsarray of RedactionOn the edit's own timeline
max items20typestringrequiredbox = solid black bar
one of"blur","box"shapestringone of"rect","ellipse"· default"rect"xnumberFraction of the final frame width
min0· max1ynumbermin0· max1wnumbermin0· max1hnumbermin0· max1startnumberSeconds
default0endnumberSeconds; null = until the end
trackarray of objectKeyframes for moving content, in time order; the region glides between them (instead of x, y, w, h)
max items50timenumberrequiredSeconds on the output timeline
min0xnumberrequiredmin0· max1ynumberrequiredmin0· max1wnumberrequiredmin0· max1hnumberrequiredmin0· max1
metadataMetadataCustomer key-value pairs (up to 20 keys, 4 KB total). Keys are 1-40 [A-Za-z0-9_.-]; values are strings up to 500 chars.
{
"edit": {
"clips": [
{
"videoId": "string",
"start": 0,
"end": 0,
"transition": {
"type": "fade",
"seconds": 0.5
}
}
]
},
"fileName": "string",
"maxHeight": 1080,
"redactions": [
{
"type": "blur",
"shape": "rect",
"x": 0,
"y": 0,
"w": 0,
"h": 0,
"start": 0,
"end": 0,
"track": [
{
"time": 0,
"x": 0,
"y": 0,
"w": 0,
"h": 0
}
]
}
],
"metadata": {}
}Clipobject
videoIdstringrequiredOne of your finished (SUCCEEDED) videos; may repeat
pattern"^[a-f0-9]{32}$"startnumberSeconds into that video
min0· default0endnumberSeconds into that video; null = to its end
transitionobjectHow this clip starts from the previous one (hard cut if omitted; not on the first clip)
typestringone of"fade","fadeblack"· default"fade"secondsnumbermin0.1· max3· default0.5
{
"videoId": "string",
"start": 0,
"end": 0,
"transition": {
"type": "fade",
"seconds": 0.5
}
}Trimobject
Keep only part of the upload (frame-accurate, no extra encode). Redaction times are on the trimmed timeline.
startnumbermin0· default0endnumbernull = to the end
{
"start": 0,
"end": 0
}VideoSummaryobject
videoIdstringfileNamestringcameraFileNamestringcreatedAtintegerUnix time
sizeBytesintegerstatestringCall GET /videos/{id} for live status
one of"UPLOADING","READY_TO_START","SUBMITTED"redactionsintegeroriginalDeletedbooleanclipsintegerPresent for edits
reprocessOfstringtrimTrimKeep only part of the upload (frame-accurate, no extra encode). Redaction times are on the trimmed timeline.
startnumbermin0· default0endnumbernull = to the end
metadataMetadataCustomer key-value pairs (up to 20 keys, 4 KB total). Keys are 1-40 [A-Za-z0-9_.-]; values are strings up to 500 chars.
{
"videoId": "string",
"fileName": "string",
"cameraFileName": "string",
"createdAt": 0,
"sizeBytes": 0,
"state": "UPLOADING",
"redactions": 0,
"originalDeleted": false,
"clips": 0,
"reprocessOf": "string",
"trim": {
"start": 0,
"end": 0
},
"metadata": {}
}Redactionobject
A fixed box (x, y, w, h) or a moving one (track). Not both.
typestringrequiredbox = solid black bar
one of"blur","box"shapestringone of"rect","ellipse"· default"rect"xnumberFraction of the final frame width
min0· max1ynumbermin0· max1wnumbermin0· max1hnumbermin0· max1startnumberSeconds
default0endnumberSeconds; null = until the end
trackarray of objectKeyframes for moving content, in time order; the region glides between them (instead of x, y, w, h)
max items50timenumberrequiredSeconds on the output timeline
min0xnumberrequiredmin0· max1ynumberrequiredmin0· max1wnumberrequiredmin0· max1hnumberrequiredmin0· max1
{
"type": "blur",
"shape": "rect",
"x": 0,
"y": 0,
"w": 0,
"h": 0,
"start": 0,
"end": 0,
"track": [
{
"time": 0,
"x": 0,
"y": 0,
"w": 0,
"h": 0
}
]
}Errors
Every error has the same JSON body:
{
"message": "Daily limit reached (50 videos per 24 hours); try again later",
"error": "DAILY_LIMIT",
"requestId": "c0a8f1e2-5b7d-4c2a-8e6f-1b0d9c7a5e3f"
}
error: a stable code. Write your code against this. New codes may be added; handle unknown ones by HTTP status.message: human-readable and safe to show to users. Its wording may change.requestId: include it when you report a problem.details(validation errors only): every problem found, as{field, message}. See Validation.
| HTTP | error | Meaning | What to do |
|---|---|---|---|
| 400 | INVALID_REQUEST | Invalid body or parameter; the message names the field. | Fix the request. |
| 401 | UNAUTHORIZED | Missing, unknown or revoked API key. | Check the Authorization header. |
| 403 | MISSING_AUTHENTICATION | The request didn't reach the API with a key, usually because the base URL is missing its /v1 path. | Check the base URL. |
| 403 | FORBIDDEN | Credentials not allowed. | Check your key. |
| 403 | REQUEST_REJECTED | Rejected before reaching the API, almost always a base URL missing /v1. | Check the base URL. |
| 404 | NOT_FOUND | No such video for you. | Check the ID. |
| 404 | ROUTE_NOT_FOUND | No such path. | Check the URL. |
| 405 | METHOD_NOT_ALLOWED | Wrong HTTP method; the message lists the right one. | Use the listed method. |
| 409 | UPLOAD_INCOMPLETE | Parts missing or the wrong size. | Upload the missing parts, then complete again. |
| 409 | ALREADY_COMPLETED | The upload already completed. | Use cancel to stop processing. |
| 409 | ORIGINAL_UNAVAILABLE | Reprocess: the original is missing or was deleted. | Upload again. |
| 409 | EDIT_SOURCE_NOT_READY | An edit clip's video hasn't finished. | Wait for SUCCEEDED. |
| 409 | NOT_AN_UPLOAD | An upload action on an edit. | Use cancel or reprocess. |
| 409 | IDEMPOTENCY_KEY_REUSED | Same Idempotency-Key, different request. | Use a new key. |
| 409 | IDEMPOTENCY_IN_PROGRESS | A request with this Idempotency-Key is still processing. | Retry in a moment. |
| 409 | INVALID_STATE | Not possible in the video's current state. | Read the message. |
| 413 | TOO_LARGE | The JSON body is too large. | Send less. |
| 429 | RATE_LIMITED | More than 5 requests per second. | Retry with backoff. |
| 429 | QUOTA_EXCEEDED | Daily request quota used up. | Try again tomorrow. |
| 429 | DAILY_LIMIT | Daily new-video limit reached. | Wait; it's a rolling 24 hours. |
| 403 | PLAN_UPGRADE_REQUIRED | The request uses something your plan doesn't include: a higher quality than its maximum (Free 720p, Starter 1080p), redaction, screen recording (camera, streaming uploads). details names the field. | Remove it, lower maxHeight, or upgrade. |
| 403 | PLAN_LIMIT_REACHED | Free plan only: this month's processing minutes or the storage allowance is used up. Nothing is deleted. | Delete videos you don't need, wait for the 1st (UTC), or upgrade. |
| 429 | VIDEOS_PAUSED | New videos are paused for your account. | Contact the service owner. |
| 502 | UPSTREAM_ERROR | An internal inconsistency. | Report it with the requestId. |
| 503 | UNAVAILABLE | A temporary failure. | Retry with backoff. |
RATE_LIMITED, UNAVAILABLE and network errors with exponential backoff (e.g. 1 s, 2 s, 4 s… with jitter). Don't retry other 4xx errors unchanged.Limits
| Limit | Default |
|---|---|
| Requests | 5 per second per key (bursts of 10) |
| Daily requests | 5,000 per key |
| New videos | 50 per rolling 24 hours (uploads, edits and reprocesses each count) |
| File size | 20 GiB per file |
| Duration | 8 hours per video |
| Upload URL lifetime | 15 minutes |
| Download URL lifetime | 15 minutes |
| Streaming link lifetime | 6 hours (Pro and Scale) |
| Top quality by plan | Free 720p · Starter 1080p · Pro and Scale 4K |
| Monthly processing and storage | Your plan's allowance (see pricing). Free stops there; paid plans continue and the extra is billed. |
| Redactions | 20 per video |
| Edit clips | 50 per edit; transitions 0.1–3 s |
Your account may have different limits; ask the service owner.
Best practices
- Keep the key server-side, and pass only pre-signed URLs to browsers and apps.
- Store the
videoIdas soon as you create a video: it's how you resume, check status, or abort. - Retry safely. Send an
Idempotency-Keyheader withPOST /videos,POST /videos/{id}/complete, andPOST /videos/{id}/reprocess. Part uploads can be retried with fresh URLs. See Idempotency. - Poll politely: every 5–15 seconds, and stop at a final status (
SUCCEEDED,FAILED,TIMED_OUT,ABORTED). - Fetch download links when needed. They expire after 15 minutes, so don't cache them.
- Handle errors by the
errorcode, not by the message text.
Changelog
| Version | Changes |
|---|---|
| v1 | Uploads (including screen + camera and while recording), processing options (quality, trim, redactions), edits (stitch, splice, cut), reprocess, cancel, structured errors, up-front validation with details, and dry runs. |
Changes within v1 only add things (new fields, new error codes) and never remove or rename existing ones.