curl --request POST \
--url https://api.tella.com/v1/videos/{id}/clips/{clipId}/layouts \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"crop": {
"bottom": 1,
"left": 1,
"right": 1,
"top": 1
},
"durationMs": 4503599627370596,
"layout": {
"kind": "fullscreen"
},
"media": {
"sourceId": "<string>",
"type": "image"
},
"popOut": {
"amount": 0.25,
"color": "<string>",
"outlineColor": "<string>",
"outlineWidth": 0.025
},
"startTimeMs": 4503599627370495
}
'import requests
url = "https://api.tella.com/v1/videos/{id}/clips/{clipId}/layouts"
payload = {
"crop": {
"bottom": 1,
"left": 1,
"right": 1,
"top": 1
},
"durationMs": 4503599627370596,
"layout": { "kind": "fullscreen" },
"media": {
"sourceId": "<string>",
"type": "image"
},
"popOut": {
"amount": 0.25,
"color": "<string>",
"outlineColor": "<string>",
"outlineWidth": 0.025
},
"startTimeMs": 4503599627370495
}
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({
crop: {bottom: 1, left: 1, right: 1, top: 1},
durationMs: 4503599627370596,
layout: {kind: 'fullscreen'},
media: {sourceId: '<string>', type: 'image'},
popOut: {amount: 0.25, color: '<string>', outlineColor: '<string>', outlineWidth: 0.025},
startTimeMs: 4503599627370495
})
};
fetch('https://api.tella.com/v1/videos/{id}/clips/{clipId}/layouts', 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.tella.com/v1/videos/{id}/clips/{clipId}/layouts",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'crop' => [
'bottom' => 1,
'left' => 1,
'right' => 1,
'top' => 1
],
'durationMs' => 4503599627370596,
'layout' => [
'kind' => 'fullscreen'
],
'media' => [
'sourceId' => '<string>',
'type' => 'image'
],
'popOut' => [
'amount' => 0.25,
'color' => '<string>',
'outlineColor' => '<string>',
'outlineWidth' => 0.025
],
'startTimeMs' => 4503599627370495
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: application/json"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api.tella.com/v1/videos/{id}/clips/{clipId}/layouts"
payload := strings.NewReader("{\n \"crop\": {\n \"bottom\": 1,\n \"left\": 1,\n \"right\": 1,\n \"top\": 1\n },\n \"durationMs\": 4503599627370596,\n \"layout\": {\n \"kind\": \"fullscreen\"\n },\n \"media\": {\n \"sourceId\": \"<string>\",\n \"type\": \"image\"\n },\n \"popOut\": {\n \"amount\": 0.25,\n \"color\": \"<string>\",\n \"outlineColor\": \"<string>\",\n \"outlineWidth\": 0.025\n },\n \"startTimeMs\": 4503599627370495\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "Bearer <token>")
req.Header.Add("Content-Type", "application/json")
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.tella.com/v1/videos/{id}/clips/{clipId}/layouts")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"crop\": {\n \"bottom\": 1,\n \"left\": 1,\n \"right\": 1,\n \"top\": 1\n },\n \"durationMs\": 4503599627370596,\n \"layout\": {\n \"kind\": \"fullscreen\"\n },\n \"media\": {\n \"sourceId\": \"<string>\",\n \"type\": \"image\"\n },\n \"popOut\": {\n \"amount\": 0.25,\n \"color\": \"<string>\",\n \"outlineColor\": \"<string>\",\n \"outlineWidth\": 0.025\n },\n \"startTimeMs\": 4503599627370495\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.tella.com/v1/videos/{id}/clips/{clipId}/layouts")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"crop\": {\n \"bottom\": 1,\n \"left\": 1,\n \"right\": 1,\n \"top\": 1\n },\n \"durationMs\": 4503599627370596,\n \"layout\": {\n \"kind\": \"fullscreen\"\n },\n \"media\": {\n \"sourceId\": \"<string>\",\n \"type\": \"image\"\n },\n \"popOut\": {\n \"amount\": 0.25,\n \"color\": \"<string>\",\n \"outlineColor\": \"<string>\",\n \"outlineWidth\": 0.025\n },\n \"startTimeMs\": 4503599627370495\n}"
response = http.request(request)
puts response.read_body{
"layout": {
"id": "<string>",
"media": [
{
"type": "image",
"height": 0,
"imageUrl": "<string>",
"muted": true,
"slot": "screen",
"sourceId": "<string>",
"startTimeMs": 123,
"width": 0
}
],
"cameraStyle": "none",
"crop": {
"bottom": 1,
"left": 1,
"right": 1,
"top": 1
},
"durationMs": 5000,
"followsBase": true,
"layout": {
"kind": "fullscreen",
"screenFit": "cover",
"style": "regular"
},
"popOut": {
"amount": 0.25,
"background": "original",
"color": "<string>",
"outlineColor": "<string>",
"outlineWidth": 0.025
},
"startTimeMs": 0,
"transitionStyle": "spring"
}
}{
"docsUrl": "https://docs.tella.com/",
"error": "bad_request",
"message": "The request was malformed or contained invalid parameters."
}{
"docsUrl": "https://docs.tella.com/",
"error": "unauthorized",
"message": "Authentication is required. Provide a valid API key."
}{
"docsUrl": "https://docs.tella.com/",
"error": "forbidden",
"message": "You don't have permission to access this resource."
}{
"docsUrl": "https://docs.tella.com/",
"error": "not_found",
"message": "The requested resource was not found."
}{
"docsUrl": "https://docs.tella.com/",
"error": "conflict",
"message": "The request conflicts with the resource's current state, e.g. an Idempotency-Key whose first request is still in progress. Retry once it settles."
}{
"docsUrl": "https://docs.tella.com/",
"error": "rate_limited",
"message": "You have exceeded the rate limit. Please slow down."
}{
"docsUrl": "https://docs.tella.com/",
"error": "server_error",
"message": "An unexpected error occurred"
}{
"docsUrl": "https://docs.tella.com/",
"error": "not_implemented",
"message": "The requested operation is not implemented."
}{
"docsUrl": "https://docs.tella.com/",
"error": "unavailable",
"message": "A dependency was unavailable and the request was not executed. Safe to resend unchanged after the Retry-After delay."
}Add a layout to a clip
Adds a layout to a clip. Provide both startTimeMs and durationMs for a time-ranged layout, or omit both for a clip-spanning layout.
curl --request POST \
--url https://api.tella.com/v1/videos/{id}/clips/{clipId}/layouts \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"crop": {
"bottom": 1,
"left": 1,
"right": 1,
"top": 1
},
"durationMs": 4503599627370596,
"layout": {
"kind": "fullscreen"
},
"media": {
"sourceId": "<string>",
"type": "image"
},
"popOut": {
"amount": 0.25,
"color": "<string>",
"outlineColor": "<string>",
"outlineWidth": 0.025
},
"startTimeMs": 4503599627370495
}
'import requests
url = "https://api.tella.com/v1/videos/{id}/clips/{clipId}/layouts"
payload = {
"crop": {
"bottom": 1,
"left": 1,
"right": 1,
"top": 1
},
"durationMs": 4503599627370596,
"layout": { "kind": "fullscreen" },
"media": {
"sourceId": "<string>",
"type": "image"
},
"popOut": {
"amount": 0.25,
"color": "<string>",
"outlineColor": "<string>",
"outlineWidth": 0.025
},
"startTimeMs": 4503599627370495
}
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({
crop: {bottom: 1, left: 1, right: 1, top: 1},
durationMs: 4503599627370596,
layout: {kind: 'fullscreen'},
media: {sourceId: '<string>', type: 'image'},
popOut: {amount: 0.25, color: '<string>', outlineColor: '<string>', outlineWidth: 0.025},
startTimeMs: 4503599627370495
})
};
fetch('https://api.tella.com/v1/videos/{id}/clips/{clipId}/layouts', 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.tella.com/v1/videos/{id}/clips/{clipId}/layouts",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'crop' => [
'bottom' => 1,
'left' => 1,
'right' => 1,
'top' => 1
],
'durationMs' => 4503599627370596,
'layout' => [
'kind' => 'fullscreen'
],
'media' => [
'sourceId' => '<string>',
'type' => 'image'
],
'popOut' => [
'amount' => 0.25,
'color' => '<string>',
'outlineColor' => '<string>',
'outlineWidth' => 0.025
],
'startTimeMs' => 4503599627370495
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: application/json"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api.tella.com/v1/videos/{id}/clips/{clipId}/layouts"
payload := strings.NewReader("{\n \"crop\": {\n \"bottom\": 1,\n \"left\": 1,\n \"right\": 1,\n \"top\": 1\n },\n \"durationMs\": 4503599627370596,\n \"layout\": {\n \"kind\": \"fullscreen\"\n },\n \"media\": {\n \"sourceId\": \"<string>\",\n \"type\": \"image\"\n },\n \"popOut\": {\n \"amount\": 0.25,\n \"color\": \"<string>\",\n \"outlineColor\": \"<string>\",\n \"outlineWidth\": 0.025\n },\n \"startTimeMs\": 4503599627370495\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "Bearer <token>")
req.Header.Add("Content-Type", "application/json")
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.tella.com/v1/videos/{id}/clips/{clipId}/layouts")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"crop\": {\n \"bottom\": 1,\n \"left\": 1,\n \"right\": 1,\n \"top\": 1\n },\n \"durationMs\": 4503599627370596,\n \"layout\": {\n \"kind\": \"fullscreen\"\n },\n \"media\": {\n \"sourceId\": \"<string>\",\n \"type\": \"image\"\n },\n \"popOut\": {\n \"amount\": 0.25,\n \"color\": \"<string>\",\n \"outlineColor\": \"<string>\",\n \"outlineWidth\": 0.025\n },\n \"startTimeMs\": 4503599627370495\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.tella.com/v1/videos/{id}/clips/{clipId}/layouts")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"crop\": {\n \"bottom\": 1,\n \"left\": 1,\n \"right\": 1,\n \"top\": 1\n },\n \"durationMs\": 4503599627370596,\n \"layout\": {\n \"kind\": \"fullscreen\"\n },\n \"media\": {\n \"sourceId\": \"<string>\",\n \"type\": \"image\"\n },\n \"popOut\": {\n \"amount\": 0.25,\n \"color\": \"<string>\",\n \"outlineColor\": \"<string>\",\n \"outlineWidth\": 0.025\n },\n \"startTimeMs\": 4503599627370495\n}"
response = http.request(request)
puts response.read_body{
"layout": {
"id": "<string>",
"media": [
{
"type": "image",
"height": 0,
"imageUrl": "<string>",
"muted": true,
"slot": "screen",
"sourceId": "<string>",
"startTimeMs": 123,
"width": 0
}
],
"cameraStyle": "none",
"crop": {
"bottom": 1,
"left": 1,
"right": 1,
"top": 1
},
"durationMs": 5000,
"followsBase": true,
"layout": {
"kind": "fullscreen",
"screenFit": "cover",
"style": "regular"
},
"popOut": {
"amount": 0.25,
"background": "original",
"color": "<string>",
"outlineColor": "<string>",
"outlineWidth": 0.025
},
"startTimeMs": 0,
"transitionStyle": "spring"
}
}{
"docsUrl": "https://docs.tella.com/",
"error": "bad_request",
"message": "The request was malformed or contained invalid parameters."
}{
"docsUrl": "https://docs.tella.com/",
"error": "unauthorized",
"message": "Authentication is required. Provide a valid API key."
}{
"docsUrl": "https://docs.tella.com/",
"error": "forbidden",
"message": "You don't have permission to access this resource."
}{
"docsUrl": "https://docs.tella.com/",
"error": "not_found",
"message": "The requested resource was not found."
}{
"docsUrl": "https://docs.tella.com/",
"error": "conflict",
"message": "The request conflicts with the resource's current state, e.g. an Idempotency-Key whose first request is still in progress. Retry once it settles."
}{
"docsUrl": "https://docs.tella.com/",
"error": "rate_limited",
"message": "You have exceeded the rate limit. Please slow down."
}{
"docsUrl": "https://docs.tella.com/",
"error": "server_error",
"message": "An unexpected error occurred"
}{
"docsUrl": "https://docs.tella.com/",
"error": "not_implemented",
"message": "The requested operation is not implemented."
}{
"docsUrl": "https://docs.tella.com/",
"error": "unavailable",
"message": "A dependency was unavailable and the request was not executed. Safe to resend unchanged after the Retry-After delay."
}Authorizations
API key obtained from your Tella account settings
Path Parameters
Video identifier
"vid_abc123def456"
Clip identifier
"cl_xyz789ghi012"
Body
Add a layout to a clip
The camera's frame shape, as the editor's Camera style picker sets it, overriding the named layout's own: circle, square, portrait, none (a rectangle in the camera slot's aspect, rounded by the clip's corners, e.g. a squircle when corners are squircle), cutout (the presenter cut out of the camera feed with no frame or room, in the layout's camera position — e.g. camera-bubble with style: full and position: bottom-left for a full-screen recording with a cut-out presenter in the corner; it switches pop-out off), blob or blob-outline. It shapes the camera bubble and the pop-out frame. Null clears it so the layout's own shape applies; omitted leaves it as it is. Needs a camera recording and a named layout that frames the camera (not cut-out, screen-only or custom; on a custom layout set presentation.shape).
none, circle, square, portrait, cutout, blob, blob-outline Crop the clip's screen recording for this layout only — the editor's crop modal on “This layout only”. Other layouts keep following the clip's crop; null makes this layout follow it again. Masks that start in the layout stay anchored to their content, and a custom layout's screen box keeps its on-canvas scale unless the same request writes layout geometry. Needs a screen recording in the clip. To crop the recording across the whole clip use auto-crop.
Hide child attributes
Hide child attributes
Pixels of the screen recording removed from its bottom edge
x >= 0Pixels of the screen recording removed from its left edge
x >= 0Pixels of the screen recording removed from its right edge
x >= 0Pixels of the screen recording removed from its top edge
x >= 0Optional. Provide together with startTimeMs for a time-ranged layout. Minimum 200ms.
200 <= x <= 9007199254740991Layout shape. May be omitted when media is provided to use the default full-frame B-roll layout.
- Fullscreen
- Middle
- Side by Side
- TV Presenter
- Cut Out
- Camera Bubble
- Camera Only
- Screen Only
- Custom
Hide child attributes
Hide child attributes
fullscreen Only honoured for basic-subject clips (Tella renders the screen fullscreen). Defaults to cover.
cover, letterbox regular (default) is plain fullscreen; stretch punches in to 1.15x. On basic-subject clips stretch needs uploaded footage (not a screen recording).
regular, stretch Optional media content to show during this layout. Only valid on time-ranged layouts.
- ClipMediaImage
- ClipMediaVideo
- ClipMediaRecording
Hide child attributes
Hide child attributes
Source ID from POST /v1/sources (kind: image).
1image Which slot the media fills: screen (the subject/main frame, the default) or camera (the bubble/presentation slot — a media bubble over the recording behind it). A camera slot requires a layout that renders the camera (e.g. camera-bubble, side-by-side, tv-presenter).
screen, camera Pop the presenter out of the camera frame. Omitted or null leaves it off.
Hide child attributes
Hide child attributes
How far the frame shrinks below the presenter, as a fraction of the camera slot (0–0.5). Defaults to 0.22.
0 <= x <= 0.5What fills the frame behind the presenter: original keeps the camera feed, color paints color. Defaults to original.
original, color Frame fill when background is color, as #RRGGBB or #RRGGBBAA. Defaults to #6366F1.
^#[0-9A-Fa-f]{6}([0-9A-Fa-f]{2})?$Outline colour as #RRGGBB or #RRGGBBAA. Defaults to #FFFFFF.
^#[0-9A-Fa-f]{6}([0-9A-Fa-f]{2})?$Outline around the frame as a fraction of its width (0–0.05); 0 draws none. Defaults to 0.
0 <= x <= 0.05Optional. Provide together with durationMs for a time-ranged layout. In ms on the clip's playback timeline (cuts applied).
0 <= x <= 9007199254740991Optional. Only valid on time-ranged layouts. Defaults to the user's saved layout intro transition when omitted. The user's saved layout outro transition is applied when the following layout begins or resumes.
spring, hardCut Response
OK
A layout on a clip
A layout attached to a clip. Times are in ms on the clip's playback timeline (cuts applied). Internally the layout stays anchored to the footage, so it doesn't drift when cuts change later.
Hide child attributes
Hide child attributes
Layout ID
B-roll media on this layout — one entry per filled slot (screen and/or camera). Empty on the base layout and on layouts whose underlying section carries no media. Each entry's slot says which slot it fills.
Hide child attributes
Hide child attributes
image, video Image height in pixels (image media only).
-9007199254740991 <= x <= 9007199254740991Hosted image URL, for image media. Read-only: not accepted as input — pass sourceId instead.
True when video media plays with its own audio silenced.
Which slot the media fills: screen (the subject/main frame, the default) or camera (the bubble/presentation slot — a media bubble over the recording behind it). A camera slot requires a layout that renders the camera (e.g. camera-bubble, side-by-side, tv-presenter).
screen, camera Source ID the media was created from. Absent for image media added in the editor.
Video media only: where it starts in its source, in ms. Absent when it plays from the start.
Image width in pixels (image media only).
-9007199254740991 <= x <= 9007199254740991The camera's frame shape, as the editor's Camera style picker sets it, overriding the named layout's own: circle, square, portrait, none (a rectangle in the camera slot's aspect, rounded by the clip's corners, e.g. a squircle when corners are squircle), cutout (the presenter cut out of the camera feed with no frame), blob or blob-outline. It shapes the camera bubble and the pop-out frame. Absent when the layout's own shape applies; custom layouts report it as presentation.shape instead.
none, circle, square, portrait, cutout, blob, blob-outline The crop of the clip's screen recording while this layout shows — the layout's own crop, else the clip's. Absent when the recording is uncropped.
Hide child attributes
Hide child attributes
Pixels of the screen recording removed from its bottom edge
x >= 0Pixels of the screen recording removed from its left edge
x >= 0Pixels of the screen recording removed from its right edge
x >= 0Pixels of the screen recording removed from its top edge
x >= 0Layout duration in ms. Omitted for clip-spanning layouts.
x >= 05000
True when this layout follows the clip's base layout: updating the base layout changes it too, so it needs no update of its own. False when it has its own layout. Absent on the base layout itself.
Structured layout for this section. Custom layouts, whether arranged in the editor or written through the API, report their geometry as kind: custom. Null only for internal layouts outside the public vocabulary.
- Fullscreen
- Middle
- Side by Side
- TV Presenter
- Cut Out
- Camera Bubble
- Camera Only
- Screen Only
- Custom
Hide child attributes
Hide child attributes
fullscreen Only honoured for basic-subject clips (Tella renders the screen fullscreen). Defaults to cover.
cover, letterbox regular (default) is plain fullscreen; stretch punches in to 1.15x. On basic-subject clips stretch needs uploaded footage (not a screen recording).
regular, stretch The camera's pop-out while this layout shows. Absent when it is off.
Hide child attributes
Hide child attributes
How far the frame shrinks below the presenter, as a fraction of the camera slot (0–0.5). Defaults to 0.22.
0 <= x <= 0.5What fills the frame behind the presenter: original keeps the camera feed, color paints color. Defaults to original.
original, color Frame fill when background is color, as #RRGGBB or #RRGGBBAA. Defaults to #6366F1.
^#[0-9A-Fa-f]{6}([0-9A-Fa-f]{2})?$Outline colour as #RRGGBB or #RRGGBBAA. Defaults to #FFFFFF.
^#[0-9A-Fa-f]{6}([0-9A-Fa-f]{2})?$Outline around the frame as a fraction of its width (0–0.05); 0 draws none. Defaults to 0.
0 <= x <= 0.05Layout start time in ms on the clip's playback timeline (cuts applied). Omitted for clip-spanning layouts.
x >= 00
Transition into this layout. Omitted on the base layout, or when the section uses an internal style not exposed via the public API.
spring, hardCut Was this page helpful?