Generations
A generation job runs one model once. Submitting returns a job in queued
state along with the credit estimate that was reserved; you poll until it
reaches a terminal state and read resultUrls / resultStorageIds.
Every generation flows through this resource — including
Applications, whose run endpoint returns a
standard generation job.
The generation job object
| Field | Type | Description |
|---|---|---|
id | string | Job id (UUID). |
model | string | Model id the job ran on. |
kind | enum | video · image · audio · upscale. |
status | enum | queued · running · succeeded · failed · canceled. |
params | object | The parameters as submitted. See Generation parameters. |
resultUrls | string[] | null | Freshly signed media URLs, present once succeeded. These expire — re-fetch the job rather than storing them. |
resultStorageIds | string[] | null | The results persisted into your storage. Durable; reuse these as inputs instead of re-uploading resultUrls. |
errorMessage | string | null | Why it failed, when status is failed. |
costCredits | number | null | Credits actually charged. null until the job settles. |
createdAt | string | ISO 8601. |
completedAt | string | null | ISO 8601, once terminal. |
projectId | string | null | The project the job is billed and scoped to. |
cutId | string | null | The cut it was attributed to, if any. |
:::tip Chain jobs with resultStorageIds, not resultUrls
resultStorageIds are permanent handles that every media-taking parameter
accepts. Feeding one generation's output into the next needs no download,
re-upload, or URL juggling.
:::
POST /v1/generations
Submit a job. Returns immediately with the job in queued — poll
GET /v1/generations/:id for the result.
Body
| Name | Type | Required | Description |
|---|---|---|---|
model | string | yes | Model id from GET /v1/models. |
params | object | yes | Must match the model's kind and capabilities. See below. |
projectId | string | no | Bill this job to a shared project — the owner pays and you're recorded as the actor. Defaults to your own default project (self-billed). |
cutId | string | no | Attribute the job to a cut within the project. |
Returns { "job": GenerationJob, "estimatedCredits": number }.
estimatedCredits is what was checked against the balance. The final charge
lands on costCredits.
curl -s -X POST https://api.corte.so/v1/generations \
-H "Authorization: Bearer corte_sk_…" \
-H "Content-Type: application/json" \
-d '{
"model": "kling-3",
"params": {
"kind": "video",
"prompt": "Slow dolly-in on a foggy ridgeline at dawn",
"duration": 5,
"aspectRatio": "16:9",
"resolution": "1080p"
}
}'
{
"job": {
"id": "5f2b9c14-8a3d-4b21-9e77-0c4a1d6f8e02",
"model": "kling-3",
"kind": "video",
"status": "queued",
"params": {
"kind": "video",
"prompt": "Slow dolly-in on a foggy ridgeline at dawn",
"duration": 5,
"aspectRatio": "16:9",
"resolution": "1080p"
},
"resultUrls": null,
"resultStorageIds": null,
"errorMessage": null,
"costCredits": null,
"createdAt": "2026-07-28T14:03:11.482Z",
"completedAt": null,
"projectId": "1b6d4f90-77c2-4a5e-b3d8-2e9a0c15f7b4",
"cutId": null
},
"estimatedCredits": 85
}
Errors
| Code | When |
|---|---|
invalid_params | model or params missing, params.kind doesn't match the model, a value outside the model's capabilities, or an unknown/uncommitted storageId. |
not_found | Unknown or disabled model id. |
plan_required | The model is paidOnly and the paying account is on Free. |
insufficient_credits | Your balance can't cover estimatedCredits. Nothing charged. |
owner_insufficient_credits | Same, but the project owner is short. |
forbidden | Generation is disabled for the workspace. |
Generation parameters
params.kind must equal the model's kind. Every value is validated against
the model's declared capabilities before
the job is created, so mistakes cost nothing.
All *StorageId / *StorageIds fields must be committed objects you can
read — yours, or linked to a shared project you belong to. See
Files.
kind: "video"
| Name | Type | Required | Description |
|---|---|---|---|
kind | "video" | yes | |
prompt | string | yes¹ | What to generate. |
duration | number | yes | Seconds. Must be a member of the model's durations. |
aspectRatio | string | yes | Must be in the model's aspectRatios. |
resolution | string | no | Must be in the model's resolutions. Also selects the creditsPerSecond rate. |
sourceVideoStorageId | string | cond. | Required when requiresSourceVideo — video-to-video, reframe, lip sync. |
startFrameStorageId | string | no | First frame. Needs supportsFirstFrame. |
endFrameStorageId | string | no | Last frame. Needs supportsLastFrame. |
referenceImageStorageIds | string[] | no | Up to maxReferenceImages. |
referenceVideoStorageIds | string[] | no | Up to maxReferenceVideos. |
referenceAudioStorageIds | string[] | no | Up to maxReferenceAudios. |
generateAudio | boolean | no | Soundtrack on or off. false on a supportsAudioToggle model returns a video-only file — the pick for scroll-scrubbed or looping backgrounds. |
¹ Optional only when the model has requiresSourceVideo: true. Models with
requiresReferenceImage: true additionally need startFrameStorageId or at
least one referenceImageStorageIds.
{
"model": "kling-3",
"params": {
"kind": "video",
"prompt": "The product rotates slowly under warm studio light",
"duration": 5,
"aspectRatio": "9:16",
"startFrameStorageId": "b0e7c9a1-4d52-4f0e-8c31-77a2e5d9f6b8"
}
}
kind: "image"
| Name | Type | Required | Description |
|---|---|---|---|
kind | "image" | yes | |
prompt | string | yes | What to generate. |
aspectRatio | string | yes | Must be in the model's aspectRatios. |
numImages | number | yes | Integer, 1 to the model's maxImages. Multiplies the cost. |
resolution | string | no | Must be in the model's resolutions. |
quality | string | no | Must be in the model's qualities. |
referenceStorageIds | string[] | no | Image conditioning. Needs supportsImageReference. |
{
"model": "nano-banana-2",
"params": {
"kind": "image",
"prompt": "Editorial flat lay of a trail-running shoe on slate, cold morning light",
"aspectRatio": "4:5",
"numImages": 2
}
}
kind: "audio"
Which fields apply depends on the model's capabilities.category.
| Name | Type | Required | Description |
|---|---|---|---|
kind | "audio" | yes | |
prompt | string | cond. | Required for tts, music, and sfx (unless lyrics is given). For TTS this is the text to speak. |
voice | string | no | For tts. Must be in voices; the model's defaultVoice is used when omitted. |
lyrics | string | cond. | For music models with supportsLyrics. Required on vocal-song models — those that support lyrics but not instrumentals. |
styleInstructions | string | no | For models with supportsStyleInstructions. |
instrumental | boolean | no | For models with supportsInstrumental. |
durationSeconds | number | cond. | Bounded by minSeconds / maxSeconds. Required for cleanup — it's the input's length, and the charge is derived from it. |
sourceStorageId | string | cond. | Required for cleanup and dubbing — the audio to process. |
videoStorageId | string | no | Video whose audio track is the source, for dubbing. |
targetLanguage | string | no | For dubbing. Must be in targetLanguages. |
{
"model": "minimax-speech",
"params": {
"kind": "audio",
"prompt": "Every frame you export is watermark-free. Always.",
"voice": "Wise_Woman"
}
}
TTS models bill on creditsPerThousandChars, so cost scales with the length of
prompt + lyrics.
kind: "upscale"
| Name | Type | Required | Description |
|---|---|---|---|
kind | "upscale" | yes | |
sourceStorageId | string | yes | The media to upscale. |
durationSeconds | number | yes | Input length in seconds; positive. Upscalers bill by duration, so this must be accurate. |
{
"model": "video-upscaler",
"params": {
"kind": "upscale",
"sourceStorageId": "b0e7c9a1-4d52-4f0e-8c31-77a2e5d9f6b8",
"durationSeconds": 12
}
}
GET /v1/generations/:id
Fetch one job. This is the polling endpoint.
Readable by the submitter, and by any member of the job's project.
Returns { "job": GenerationJob }.
curl -s https://api.corte.so/v1/generations/5f2b9c14-8a3d-4b21-9e77-0c4a1d6f8e02 \
-H "Authorization: Bearer corte_sk_…"
{
"job": {
"id": "5f2b9c14-8a3d-4b21-9e77-0c4a1d6f8e02",
"model": "kling-3",
"kind": "video",
"status": "succeeded",
"params": { "kind": "video", "prompt": "Slow dolly-in on a foggy ridgeline at dawn", "duration": 5, "aspectRatio": "16:9", "resolution": "1080p" },
"resultUrls": ["https://media.corte.so/…/out.mp4?X-Amz-Signature=…"],
"resultStorageIds": ["c74a1e08-9b6f-4d13-a205-e8f30b7c9d61"],
"errorMessage": null,
"costCredits": 85,
"createdAt": "2026-07-28T14:03:11.482Z",
"completedAt": "2026-07-28T14:04:47.019Z",
"projectId": "1b6d4f90-77c2-4a5e-b3d8-2e9a0c15f7b4",
"cutId": null
}
}
A failed job is still a 200 — check status:
{
"job": {
"id": "5f2b9c14-…",
"status": "failed",
"errorMessage": "Upstream provider rejected the prompt (content policy)",
"costCredits": 0,
"completedAt": "2026-07-28T14:04:02.771Z"
}
}
Polling
import time, requests
def wait(job_id, timeout=900, interval=3):
deadline = time.time() + timeout
while time.time() < deadline:
job = requests.get(
f"https://api.corte.so/v1/generations/{job_id}",
headers={"Authorization": f"Bearer {KEY}"},
).json()["job"]
if job["status"] in ("succeeded", "failed", "canceled"):
return job
time.sleep(interval)
raise TimeoutError(job_id)
Images usually settle in seconds; video in one to several minutes.
GET /v1/generations
List recent jobs, newest first.
Without projectId this returns your own jobs. With projectId it returns
every member's jobs for that project — the shared library view.
Query parameters
| Name | Type | Default | Description |
|---|---|---|---|
limit | number | 50 | Max 200. |
offset | number | 0 | Page through with limit. |
projectId | string | — | Scope to a shared project you belong to. Switches the query to all members' jobs. |
cutId | string | — | Narrow to one cut. Only applies alongside projectId. |
Returns { "jobs": GenerationJob[], "total": number } — total counts
every matching job, not the page.
curl -s "https://api.corte.so/v1/generations?limit=20" \
-H "Authorization: Bearer corte_sk_…"
{
"jobs": [
{ "id": "5f2b9c14-…", "model": "kling-3", "kind": "video", "status": "succeeded", "costCredits": 85, "…": "…" },
{ "id": "8d31a067-…", "model": "nano-banana-2", "kind": "image", "status": "succeeded", "costCredits": 24, "…": "…" }
],
"total": 214
}
POST /v1/generations/:id/cancel
Cancel a queued or running job. Best-effort with the upstream provider; the
job row is marked canceled either way.
Idempotent — cancelling an already-terminal job returns it unchanged rather than erroring.
Only the submitter may cancel their own job. For project-billed jobs submitted by someone else, only the project owner may cancel.
Returns { "job": GenerationJob }.
curl -s -X POST https://api.corte.so/v1/generations/5f2b9c14-…/cancel \
-H "Authorization: Bearer corte_sk_…"
{
"job": {
"id": "5f2b9c14-8a3d-4b21-9e77-0c4a1d6f8e02",
"status": "canceled",
"completedAt": "2026-07-28T14:03:39.204Z",
"…": "…"
}
}