API Reference
Cutline REST API
Programmatic access to the same 12-stage pipeline that powers the web app. Submit a sentence, poll the job, download the MP4. JSON in, MP4 out.
Overview
The Cutline API exposes a single asynchronous workflow: submit a job, poll for status, download when ready. All endpoints are versioned under /api/v1/.
Responses are JSON. Error responses always carry a stable, branchable code field - never branch on error message text.
Base URL
https://cutline.cloud/api/v1Content type
application/jsonAuthentication
API requests are authenticated with an API key sent in the X-API-Key header. Generate keys from your dashboard. Each key is shown once at creation time and stored as a hash on our side - keep it safe.
curl https://cutline.cloud/api/v1/generate/jobs \
-H "X-API-Key: ck_live_••••••••••••••••"Create a video
/api/v1/generateSubmits a generation job. Returns immediately with a jobId - rendering happens asynchronously in a worker. Poll status (next section) until completed or failed.
Request body
| Parameter | Type | Description |
|---|---|---|
| input* | string | Your one-sentence prompt. 5-500 characters. |
| durationSeconds | number | Target video length, 10-60. Defaults to inferred from prompt. |
| mode | string | "slideshow" (default) or "talking_object" (Veo-backed character video). |
| captions | string | "on" (default) or "off". Burned-in subtitles. |
| platform | string | "general" / "linkedin" / "twitter" / "youtube_shorts". Tunes pacing and tone. |
| assetIds | string[] | IDs from /api/assets/upload - logos, product photos, reference media. |
| brandColors | object | { primary?: hex, secondary?: hex }. Used by the visual stage. |
| callbackUrl | string | Webhook URL fired on terminal job state. See Webhooks below. |
| textModel | string | OpenRouter model ID override for this job's LLM stages. |
Example
curl -X POST https://cutline.cloud/api/v1/generate \
-H "X-API-Key: ck_live_••••••••••••••••" \
-H "Content-Type: application/json" \
-H "X-Idempotency-Key: order_12345" \
-d '{
"input": "Explain how Redis works in 30 seconds",
"durationSeconds": 30,
"captions": "on",
"platform": "youtube_shorts"
}'const res = await fetch("https://cutline.cloud/api/v1/generate", {
method: "POST",
headers: {
"X-API-Key": process.env.CUTLINE_API_KEY,
"Content-Type": "application/json",
"X-Idempotency-Key": crypto.randomUUID(),
},
body: JSON.stringify({
input: "Explain how Redis works in 30 seconds",
durationSeconds: 30,
captions: "on",
platform: "youtube_shorts",
}),
});
const { jobId } = await res.json();import os, requests, uuid
res = requests.post(
"https://cutline.cloud/api/v1/generate",
headers={
"X-API-Key": os.environ["CUTLINE_API_KEY"],
"X-Idempotency-Key": str(uuid.uuid4()),
},
json={
"input": "Explain how Redis works in 30 seconds",
"durationSeconds": 30,
"captions": "on",
"platform": "youtube_shorts",
},
)
job_id = res.json()["jobId"]Response (200)
{
"jobId": "job_01HK8Z9X2YBN5T7QM3R4S5T6"
}Poll job status
/api/v1/generate/:jobIdReturns current job state. Poll with exponential backoff (we use 2s → 4s → 8s → 15s cap in the web app). Stop polling on terminal states completed, failed, or cancelled.
{
"status": "processing",
"stage": "tts",
"stageProgress": 0.42,
"videoUrl": null,
"error": null
}{
"status": "completed",
"videoUrl": "/temp/job_01HK8Z9X2YBN5T7QM3R4S5T6.mp4",
"error": null,
"completedAt": "2026-05-10T14:23:11.842Z"
}Download the MP4
/api/v1/generate/:jobId/downloadStreams the rendered MP4 as Content-Disposition: attachment. Files are retained for VIDEO_RETENTION_HOURS (default 24h) - fetch within the window. Returns 404 VIDEO_NOT_FOUND after expiry.
curl -L \
-H "X-API-Key: ck_live_••••••••••••••••" \
-o video.mp4 \
https://cutline.cloud/api/v1/generate/$JOB_ID/downloadCancel a job
/api/v1/generate/:jobId/cancelSets a Redis cancellation flag. The orchestrator checks the flag between every pipeline stage, so cancellation is eventual - the current stage finishes before the job exits. Returns 409 JOB_CANNOT_CANCEL if the job is already in a terminal state.
List recent jobs
/api/v1/generate/jobsReturns up to 50 most recent jobs for the authenticated key, ordered by creation time descending. Pass ?limit=20 to truncate.
Webhooks
Pass callbackUrl when creating a job. When the job reaches a terminal state we POST the following payload:
{
"jobId": "job_01HK8Z9X2YBN5T7QM3R4S5T6",
"status": "completed",
"videoUrl": "/temp/job_01HK8Z9X2YBN5T7QM3R4S5T6.mp4",
"completedAt": "2026-05-10T14:23:11.842Z",
"qualityReport": {
"scriptStage": "ok",
"ttsStage": "ok",
"imageStage": "fallback_used",
"renderStage": "ok",
"retries": 1
}
}Delivery is fire-and-forget. We do not retry. 5-second timeout. Localhost URLs are rejected in production unless ALLOW_LOCALHOST_WEBHOOK=true.
Idempotency
Pass X-Idempotency-Key (max 128 chars) on POST to safely retry network failures. The same key within a 24h window returns the same jobId - duplicate work is never enqueued. We recommend a UUID per logical user-action.
Error codes
Branch on code, never on error. The error field is human copy and may be rewritten without a version bump.
| Parameter | Type | Description |
|---|---|---|
| VALIDATION_FAILED | 400 | Input validation failed. details.errors lists field-level issues. |
| INVALID_JSON | 400 | Request body isn't valid JSON. |
| BAD_REQUEST | 400 | Generic bad request. |
| AUTH_REQUIRED | 401 | Missing or invalid API key. |
| ANON_LIMIT_REACHED | 403 | Anonymous-tier quota exhausted. |
| JOB_NOT_FOUND | 404 | No job with that ID. |
| VIDEO_NOT_FOUND | 404 | Video has been cleaned up after retention window. |
| JOB_NOT_READY | 404 | Tried to download before job completed. |
| JOB_CANNOT_CANCEL | 409 | Job is already in a terminal state. |
| RATE_LIMITED | 429 | Too many requests. Check Retry-After header. |
| INTERNAL_ERROR | 500 | Unhandled error. Safe to retry with the same idempotency key. |
Rate limits
Per-IP and per-key limits, enforced by Redis with sliding windows.
- 5/hour - POST
/api/v1/generateper IP (anonymous) - 60/min - GET
/api/v1/generate/:jobIdper IP - 20/hour - POST
/api/assets/uploadper IP - Authenticated quotas are determined by your plan - see pricing.
429 responses include a Retry-After header (seconds).
Need a higher rate limit, an enterprise SLA, or custom integration support? parbhat@parbhat.work