curl --request POST \
--url https://api.framelane.io/v1/projects/{project_id}/renders \
--header 'Authorization: Bearer <token>'import requests
url = "https://api.framelane.io/v1/projects/{project_id}/renders"
headers = {"Authorization": "Bearer <token>"}
response = requests.post(url, headers=headers)
print(response.text)const options = {method: 'POST', headers: {Authorization: 'Bearer <token>'}};
fetch('https://api.framelane.io/v1/projects/{project_id}/renders', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.framelane.io/v1/projects/{project_id}/renders",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"net/http"
"io"
)
func main() {
url := "https://api.framelane.io/v1/projects/{project_id}/renders"
req, _ := http.NewRequest("POST", url, nil)
req.Header.Add("Authorization", "Bearer <token>")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://api.framelane.io/v1/projects/{project_id}/renders")
.header("Authorization", "Bearer <token>")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.framelane.io/v1/projects/{project_id}/renders")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
response = http.request(request)
puts response.read_body{
"id": "render_01J8QR2K5VKDGN2T4FBM3CZYX7",
"kind": "render",
"workspace_id": "ws_01J8QR2K5VKDGN2T4FBM3CZYX8",
"status": "queued",
"created_at": "2023-11-07T05:31:56Z",
"updated_at": "2023-11-07T05:31:56Z",
"ok": true,
"is_preview": false,
"task_type": "transcribe",
"progress_percent": 0,
"progress_stage": "compositing",
"output": {
"url": "https://cdn-user.framelane.io/render_01J.../output.mp4",
"width": 1920,
"height": 1080,
"duration": 15.3,
"size_bytes": 12582912
},
"result": {
"language_code": "en",
"text": "Welcome back to the show."
},
"error": {
"code": "invalid_source",
"message": "Source URL returned 404."
},
"metadata": {
"project_id": "proj_123",
"user_ref": "usr_abc"
},
"completed_at": "2023-11-07T05:31:56Z",
"violations": [
{
"code": "<string>",
"message": "<string>",
"severity": "error",
"path": "<string>",
"bbox": [
123
],
"time": 123
}
]
}{
"error": {
"code": "invalid_request",
"message": "<string>",
"details": {}
}
}{
"error": {
"code": "invalid_request",
"message": "<string>",
"details": {}
}
}{
"error": {
"code": "invalid_request",
"message": "<string>",
"details": {}
}
}Render the project head
Submit the current project head to the render engine as a standard (billable) render. Idempotency-Key note: reusing a key returns the original job unconditionally — the body (here, the project head at submission time) is not compared, so a key reused after the project changed still replays the earlier job rather than 409ing. The response carries an ADVISORY ok + violations[] lint of the head; the render is dispatched either way.
curl --request POST \
--url https://api.framelane.io/v1/projects/{project_id}/renders \
--header 'Authorization: Bearer <token>'import requests
url = "https://api.framelane.io/v1/projects/{project_id}/renders"
headers = {"Authorization": "Bearer <token>"}
response = requests.post(url, headers=headers)
print(response.text)const options = {method: 'POST', headers: {Authorization: 'Bearer <token>'}};
fetch('https://api.framelane.io/v1/projects/{project_id}/renders', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.framelane.io/v1/projects/{project_id}/renders",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"net/http"
"io"
)
func main() {
url := "https://api.framelane.io/v1/projects/{project_id}/renders"
req, _ := http.NewRequest("POST", url, nil)
req.Header.Add("Authorization", "Bearer <token>")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://api.framelane.io/v1/projects/{project_id}/renders")
.header("Authorization", "Bearer <token>")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.framelane.io/v1/projects/{project_id}/renders")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
response = http.request(request)
puts response.read_body{
"id": "render_01J8QR2K5VKDGN2T4FBM3CZYX7",
"kind": "render",
"workspace_id": "ws_01J8QR2K5VKDGN2T4FBM3CZYX8",
"status": "queued",
"created_at": "2023-11-07T05:31:56Z",
"updated_at": "2023-11-07T05:31:56Z",
"ok": true,
"is_preview": false,
"task_type": "transcribe",
"progress_percent": 0,
"progress_stage": "compositing",
"output": {
"url": "https://cdn-user.framelane.io/render_01J.../output.mp4",
"width": 1920,
"height": 1080,
"duration": 15.3,
"size_bytes": 12582912
},
"result": {
"language_code": "en",
"text": "Welcome back to the show."
},
"error": {
"code": "invalid_source",
"message": "Source URL returned 404."
},
"metadata": {
"project_id": "proj_123",
"user_ref": "usr_abc"
},
"completed_at": "2023-11-07T05:31:56Z",
"violations": [
{
"code": "<string>",
"message": "<string>",
"severity": "error",
"path": "<string>",
"bbox": [
123
],
"time": 123
}
]
}{
"error": {
"code": "invalid_request",
"message": "<string>",
"details": {}
}
}{
"error": {
"code": "invalid_request",
"message": "<string>",
"details": {}
}
}{
"error": {
"code": "invalid_request",
"message": "<string>",
"details": {}
}
}Authorizations
Bearer authentication header of the form Bearer <token>, where <token> is your auth token.
Headers
Path Parameters
Response
Successful Response
A freshly submitted render: the job row plus the advisory lint of its composition.
Returned only by the two submit routes (POST /v1/renders,
POST /v1/projects/{id}/renders). A subclass rather than two more fields on
:class:JobOut, because JobOut is the shape of a job row on every read route
(GET /v1/renders, all /v1/tasks routes, the MCP read tools) where a lint
result does not exist and would be permanently null.
Unique job ID. Prefix indicates type: render_ or task_.
"render_01J8QR2K5VKDGN2T4FBM3CZYX7"
Whether this is a render or a task.
render, task "render"
ID of the workspace that owns this job.
"ws_01J8QR2K5VKDGN2T4FBM3CZYX8"
Current lifecycle state: queued → processing → completed / failed / cancelled.
ingesting, queued, processing, completed, failed, cancelled "queued"
ISO-8601 UTC timestamp when the job was created.
ISO-8601 UTC timestamp of the last status change.
Advisory — the render was still accepted and dispatched. False means the composition has error-severity findings that will likely make the output wrong (a self-transition, a fully off-canvas element); it does NOT mean the submission was refused. Everything that actually blocks a render is a 422 before you ever see this field.
true
True for a cheap preview render. Previews are not billed and are excluded from the renders list.
false
For task jobs only — the specific AI operation.
transcribe "transcribe"
0–100 progress indicator updated by the render engine.
0 <= x <= 1000
Current processing phase within a job.
downloading, compositing, encoding, uploading "compositing"
Populated once status == completed. Contains the signed artifact URL.
Show child attributes
Show child attributes
Structured JSON result for tasks that return data rather than a file — transcribe returns the transcript here (text, words, utterances, and any requested analysis). Populated once status == completed; null for renders and for any job whose only artifact is the file at output.url.
{
"language_code": "en",
"text": "Welcome back to the show."
}
Populated when status == failed. Contains a structured error code.
Show child attributes
Show child attributes
Caller-supplied key-value pairs echoed back on every webhook and response.
{
"project_id": "proj_123",
"user_ref": "usr_abc"
}
ISO-8601 UTC timestamp when the job reached a terminal state.
Advisory — the render was still accepted and dispatched. Cross-element and layout findings from the same linter the preview dry-run runs, attached so you can see problems on the render you just paid for instead of only in the artifact. To catch these BEFORE spending a render, call POST /v1/preview with dry_run.Truncated to the first 40 findings (errors first) on very large compositions, with a VIOLATIONS_TRUNCATED marker carrying the full count; the free dry run is never truncated.
Show child attributes
Show child attributes

