Skip to content
Create a key

Paste a key to use it in the examples on these pages. The key stays in this browser tab. These pages never send it anywhere.

Change an edit

apply_timeline_operationsPOST/v1/projects/{projectId}/timeline/operationsBeta

Applies a list of operations to an edit in one change: cuts, order, transitions, zoom, sound, captions, texts, b-roll, music and shape.

Beta. This endpoint is supported and can gain new fields. If a change would break your code, we tell you before it takes effect and list it in the changelog.

About

Send the revision you read with get_timeline (GET /v1/projects/{projectId}/timeline) and from 1 to 50 operations: they apply in order, as one change and one new revision, or not at all. Segments, texts and b-roll are named by the ids get_timeline shows; times of segments are seconds of the source video, times of texts and b-roll are seconds of the video that comes out. Adding a video is not an operation here, because it costs credits: use add_timeline_clip (POST /v1/projects/{projectId}/timeline/clips). The answer is the edit after the change, with warnings that say what was adjusted.

Example use

An editing tool lets a person remove a long pause and add a title text. It sends both as one list of operations with the revision it read, then shows the warnings of the answer next to the new edit.

Endpoint

Code
POST https://api.gpt-video.com/v1/projects/{projectId}/timeline/operations
  • Scope clips:write.
  • Rate limit 60 requests a minute for each key and 120 for each account, shared by every write call. See rate limits.
  • Idempotency-Key is optional.

Parameters

Path

NameTypeRequiredDescription
projectIdstringRequiredThe project id. It starts with prj_. Matches ^prj_[0-9a-f]{20}$.

Headers

NameTypeRequiredDescription
Idempotency-KeystringOptionalSend the same key on every retry of this request. From 1 to 255 visible characters, such as a UUID. See idempotency.

Body

NameTypeRequiredDescription
expectedRevisionintegerRequiredThe revision of the edit you read last, from get_timeline (GET /v1/projects/{projectId}/timeline) or from the answer of your last change. If the edit changed since then, nothing is written and the answer is CONFLICT. At least 0.
operationsarray of objectsRequiredThe operations, from 1 to 50, applied in this order. The op field says which one each is. From 1 to 50 items.

Each item of operations is one of 21 variants, chosen by op. Send exactly one variant for each item.

op = "split_segment"

NameTypeRequiredDescription
operations[].opstringRequiredAlways split_segment.
operations[].segmentIdstringRequiredThe segment id. It starts with seg_. Matches ^seg_[0-9a-f]{20}$.
operations[].atSourceSecnumberRequiredWhere to cut, in seconds of the source video. More than 0.

op = "trim_segment"

NameTypeRequiredDescription
operations[].opstringRequiredAlways trim_segment.
operations[].segmentIdstringRequiredThe segment id. It starts with seg_. Matches ^seg_[0-9a-f]{20}$.
operations[].sourceStartSecnumberOptionalThe new start, in seconds of the source video. At least 0.
operations[].sourceEndSecnumberOptionalThe new end, in seconds of the source video. More than 0.

op = "remove_segments"

NameTypeRequiredDescription
operations[].opstringRequiredAlways remove_segments.
operations[].segmentIdsarray of stringsRequiredThe segment ids, from 1 to 200. From 1 to 200 items.

op = "move_segment"

NameTypeRequiredDescription
operations[].opstringRequiredAlways move_segment.
operations[].segmentIdstringRequiredThe segment id. It starts with seg_. Matches ^seg_[0-9a-f]{20}$.
operations[].beforeSegmentIdstring or nullRequiredThe segment to move it in front of; null moves it to the end. Matches ^seg_[0-9a-f]{20}$.

op = "set_transition"

NameTypeRequiredDescription
operations[].opstringRequiredAlways set_transition.
operations[].segmentIdstringRequiredThe segment id. It starts with seg_. Matches ^seg_[0-9a-f]{20}$.
operations[].kindstringRequiredOne of dip, dissolve.
operations[].secnumberRequiredHow long the transition into this segment lasts, from 0 to 3 seconds; 0 removes it. From 0 to 3.

op = "set_video_fade"

NameTypeRequiredDescription
operations[].opstringRequiredAlways set_video_fade.
operations[].edgestringRequiredOne of start, end.
operations[].secnumberRequiredHow long the fade lasts, from 0 to 3 seconds; 0 removes it. From 0 to 3.

op = "set_zoom"

NameTypeRequiredDescription
operations[].opstringRequiredAlways set_zoom.
operations[].segmentIdsarray of stringsRequiredThe segment ids, from 1 to 200. From 1 to 200 items.
operations[].factornumberRequired1 removes the zoom; up to 1.5. From 1 to 1.5.
operations[].modestringRequiredOne of static, push.

op = "set_segment_audio"

NameTypeRequiredDescription
operations[].opstringRequiredAlways set_segment_audio.
operations[].segmentIdsarray of stringsRequiredThe segment ids, from 1 to 200. From 1 to 200 items.
operations[].volumenumberOptionalFrom 0 to 2.
operations[].mutedbooleanOptional

op = "remove_silences"

NameTypeRequiredDescription
operations[].opstringRequiredAlways remove_silences.
operations[].aggressivenessstringRequiredOne of soft, standard, aggressive.

op = "remove_fillers"

NameTypeRequiredDescription
operations[].opstringRequiredAlways remove_fillers.

op = "set_captions"

NameTypeRequiredDescription
operations[].opstringRequiredAlways set_captions.
operations[].enabledbooleanOptional
operations[].stylestringOptionalOne of bold-karaoke, word-pop, boxed, outline.
operations[].optionsobjectOptionalThe caption options to change. A field you leave out stays as it is.
operations[].options.fontFamilystringOptionalThe typeface of the captions. One of Montserrat, Anton, System.
operations[].options.fontSizeintegerOptionalThe size of the caption text, from 20 to 200. From 20 to 200.
operations[].options.yPercentnumberOptionalWhere the captions sit, from 0 at the top to 100 at the bottom. From 0 to 100.
operations[].options.colorstringOptionalThe color of the words, as #RRGGBB. Matches ^#[0-9A-Fa-f]{6}$.
operations[].options.highlightColorstringOptionalThe color of the word being spoken, as #RRGGBB. Matches ^#[0-9A-Fa-f]{6}$.
operations[].options.maxWordsPerGroupintegerOptionalHow many words are on screen at once, at most, from 1 to 12. From 1 to 12.
operations[].options.maxCharsPerGroupintegerOptionalHow many characters are on screen at once, at most, from 4 to 80. From 4 to 80.
operations[].options.uppercasebooleanOptionaltrue shows the captions in capital letters.
operations[].options.maxGapSecnumberOptionalThe longest pause, in seconds, inside one group of words on screen, from 0.1 to 5. From 0.1 to 5.

op = "replace_caption_word"

NameTypeRequiredDescription
operations[].opstringRequiredAlways replace_caption_word.
operations[].fromstringRequiredFrom 1 to 200 characters.
operations[].tostringRequiredFrom 1 to 200 characters.
operations[].occurrenceintegerOptionalWhich match to replace, from 1; leave it out to replace every match. At least 1.

op = "add_text"

NameTypeRequiredDescription
operations[].opstringRequiredAlways add_text.
operations[].textstringRequiredFrom 1 to 200 characters.
operations[].presetstringRequiredOne of headline, hook, caption-note.
operations[].outputStartSecnumberRequiredA time in the video that comes out, in seconds. At least 0.
operations[].durationSecnumberRequiredA length in seconds. More than 0.
operations[].xPercentnumberOptionalThe horizontal position, from 0 to 100. From 0 to 100.
operations[].yPercentnumberOptionalThe vertical position, from 0 to 100. From 0 to 100.

op = "update_text"

NameTypeRequiredDescription
operations[].opstringRequiredAlways update_text.
operations[].textIdstringRequiredThe text id. It starts with ovl_. Matches ^ovl_[0-9a-f]{20}$.
operations[].textstringOptionalFrom 1 to 200 characters.
operations[].presetstringOptionalOne of headline, hook, caption-note.
operations[].outputStartSecnumberOptionalA time in the video that comes out, in seconds. At least 0.
operations[].durationSecnumberOptionalA length in seconds. More than 0.
operations[].xPercentnumber or nullOptionalThe horizontal position, from 0 to 100. From 0 to 100.
operations[].yPercentnumber or nullOptionalThe vertical position, from 0 to 100. From 0 to 100.

op = "remove_text"

NameTypeRequiredDescription
operations[].opstringRequiredAlways remove_text.
operations[].textIdstringRequiredThe text id. It starts with ovl_. Matches ^ovl_[0-9a-f]{20}$.

op = "add_broll"

NameTypeRequiredDescription
operations[].opstringRequiredAlways add_broll.
operations[].assetIdstringRequiredMatches ^ast_n?[0-9a-f]{20}$.
operations[].outputStartSecnumberRequiredA time in the video that comes out, in seconds. At least 0.
operations[].durationSecnumberRequiredA length in seconds. More than 0.
operations[].fitstringRequiredOne of cover, contain.

op = "update_broll"

NameTypeRequiredDescription
operations[].opstringRequiredAlways update_broll.
operations[].brollIdstringRequiredThe b-roll id. It starts with brl_. Matches ^brl_[0-9a-f]{20}$.
operations[].outputStartSecnumberOptionalA time in the video that comes out, in seconds. At least 0.
operations[].durationSecnumberOptionalA length in seconds. More than 0.
operations[].fitstringOptionalOne of cover, contain.

op = "remove_broll"

NameTypeRequiredDescription
operations[].opstringRequiredAlways remove_broll.
operations[].brollIdstringRequiredThe b-roll id. It starts with brl_. Matches ^brl_[0-9a-f]{20}$.

op = "set_music"

NameTypeRequiredDescription
operations[].opstringRequiredAlways set_music.
operations[].assetIdstring or nullRequiredThe music file; null removes the music. Matches ^ast_n?[0-9a-f]{20}$.
operations[].volumenumberOptionalFrom 0 to 0.5.

op = "set_aspect"

NameTypeRequiredDescription
operations[].opstringRequiredAlways set_aspect.
operations[].aspectstringRequiredOne of source, 9:16, 1:1, 16:9.

op = "set_reframe"

NameTypeRequiredDescription
operations[].opstringRequiredAlways set_reframe.
operations[].modestringRequiredOne of off, face, center.

Send only the fields listed here. Any other field is refused with UNKNOWN_PARAMETER.

Request

Cut a segment in two and remove another.

Request example
# Create the key once. Send this same key again if you retry this request.
IDEMPOTENCY_KEY=$(uuidgen)
curl -X POST "https://api.gpt-video.com/v1/projects/prj_b9c1f9c7873dcef51a57/timeline/operations" \
  -H "Authorization: Bearer $GPT_VIDEO_API_KEY" \
  -H "Idempotency-Key: $IDEMPOTENCY_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "expectedRevision": 1,
    "operations": [
      {
        "op": "split_segment",
        "segmentId": "seg_7279fbb508f4a22e65a4",
        "atSourceSec": 135
      },
      {
        "op": "remove_segments",
        "segmentIds": [
          "seg_a9dcee59c1fb9edb6751"
        ]
      }
    ]
  }'
// Create the key once. Send this same key again if you retry this request.
const idempotencyKey = crypto.randomUUID();
const res = await fetch("https://api.gpt-video.com/v1/projects/prj_b9c1f9c7873dcef51a57/timeline/operations", {
  method: "POST",
  headers: {
    "Authorization": `Bearer ${process.env.GPT_VIDEO_API_KEY}`,
    "Idempotency-Key": idempotencyKey,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    "expectedRevision": 1,
    "operations": [
      {
        "op": "split_segment",
        "segmentId": "seg_7279fbb508f4a22e65a4",
        "atSourceSec": 135
      },
      {
        "op": "remove_segments",
        "segmentIds": [
          "seg_a9dcee59c1fb9edb6751"
        ]
      }
    ]
  }),
});
const data = await res.json();
console.log(data);
import os
import uuid

import requests

# Create the key once. Send this same key again if you retry this request.
idempotency_key = str(uuid.uuid4())
r = requests.post(
    "https://api.gpt-video.com/v1/projects/prj_b9c1f9c7873dcef51a57/timeline/operations",
    headers={
        "Authorization": f"Bearer {os.environ['GPT_VIDEO_API_KEY']}",
        "Idempotency-Key": idempotency_key,
    },
    json={
        "expectedRevision": 1,
        "operations": [
            {
                "op": "split_segment",
                "segmentId": "seg_7279fbb508f4a22e65a4",
                "atSourceSec": 135
            },
            {
                "op": "remove_segments",
                "segmentIds": [
                    "seg_a9dcee59c1fb9edb6751"
                ]
            }
        ]
    },
)
data = r.json()
print(data)
{
  "method": "tools/call",
  "params": {
    "name": "apply_timeline_operations",
    "arguments": {
      "projectId": "prj_b9c1f9c7873dcef51a57",
      "expectedRevision": 1,
      "operations": [
        {
          "op": "split_segment",
          "segmentId": "seg_7279fbb508f4a22e65a4",
          "atSourceSec": 135
        },
        {
          "op": "remove_segments",
          "segmentIds": [
            "seg_a9dcee59c1fb9edb6751"
          ]
        }
      ]
    }
  }
}

Response

A successful call answers HTTP 200. Returns a Timeline object in timeline.

Fields to check

NameTypeDescription
timeline.idstringThe edit id. It starts with tml_.
timeline.segments[].idstringThe segment id. It starts with seg_.
timeline.texts[].idstringThe text id. It starts with ovl_.
timeline.broll[].idstringThe b-roll id. It starts with brl_.

Response example

HTTP 200
{
  "timeline": {
    "id": "tml_bbe061171a8229f8daed",
    "projectId": "prj_b9c1f9c7873dcef51a57",
    "compositionId": "cmp_cfbdd85eaaff301fa997",
    "revision": 2,
    "durationSec": 30,
    "aspect": "9:16",
    "reframe": "off",
    "captions": {
      "enabled": true,
      "style": "bold-karaoke"
    },
    "music": null,
    "fadeInSec": 0,
    "fadeOutSec": 0,
    "clips": [
      {
        "assetId": "ast_3f9a1c7e5b2d4086a1c4",
        "title": "Interview with a founder",
        "durationSec": 1800,
        "state": "ready"
      }
    ],
    "segments": [
      {
        "id": "seg_7279fbb508f4a22e65a4",
        "assetId": "ast_3f9a1c7e5b2d4086a1c4",
        "sourceStartSec": 120,
        "sourceEndSec": 135,
        "zoom": 1,
        "muted": false,
        "volume": 1,
        "outputStartSec": 0,
        "outputEndSec": 15
      },
      {
        "id": "seg_5a0e3c8f1d7b42e69c13",
        "assetId": "ast_3f9a1c7e5b2d4086a1c4",
        "sourceStartSec": 135,
        "sourceEndSec": 150,
        "zoom": 1,
        "muted": false,
        "volume": 1,
        "outputStartSec": 15,
        "outputEndSec": 30
      }
    ],
    "texts": [
      {
        "id": "ovl_49f5239b9cd73496225d",
        "text": "Paid before the product",
        "preset": "headline",
        "outputStartSec": 0,
        "durationSec": 3,
        "xPercent": null,
        "yPercent": null
      }
    ],
    "broll": [
      {
        "id": "brl_f44d18e008ec4009bf10",
        "assetId": "ast_8ac5d8b97546145cdbf6",
        "outputStartSec": 12,
        "durationSec": 4,
        "fit": "cover"
      }
    ],
    "diagnostics": []
  },
  "compositionId": "cmp_cfbdd85eaaff301fa997",
  "warnings": []
}
All response fields
NameTypeRequiredDescription
timelineTimeline objectRequiredDescribed in Timeline object.
compositionIdstringRequiredThe composition the edit compiles to, to export with create_export (POST /v1/compositions/{compositionId}/exports).
warningsarray of stringsRequiredWhat the change adjusted or could not do, in one sentence each; empty when nothing.
More examples

Change the order, the cut between two segments, the zoom and the sound

POST /v1/projects/prj_b9c1f9c7873dcef51a57/timeline/operations

Request body
{
  "expectedRevision": 1,
  "operations": [
    {
      "op": "move_segment",
      "segmentId": "seg_a9dcee59c1fb9edb6751",
      "beforeSegmentId": "seg_7279fbb508f4a22e65a4"
    },
    {
      "op": "trim_segment",
      "segmentId": "seg_a9dcee59c1fb9edb6751",
      "sourceStartSec": 302
    },
    {
      "op": "set_transition",
      "segmentId": "seg_7279fbb508f4a22e65a4",
      "kind": "dip",
      "sec": 0.5
    },
    {
      "op": "set_zoom",
      "segmentIds": [
        "seg_a9dcee59c1fb9edb6751"
      ],
      "factor": 1.2,
      "mode": "push"
    },
    {
      "op": "set_segment_audio",
      "segmentIds": [
        "seg_7279fbb508f4a22e65a4"
      ],
      "volume": 0.8
    }
  ]
}
HTTP 200
{
  "timeline": {
    "id": "tml_bbe061171a8229f8daed",
    "projectId": "prj_b9c1f9c7873dcef51a57",
    "compositionId": "cmp_cfbdd85eaaff301fa997",
    "revision": 2,
    "durationSec": 58,
    "aspect": "9:16",
    "reframe": "off",
    "captions": {
      "enabled": true,
      "style": "bold-karaoke"
    },
    "music": null,
    "fadeInSec": 0,
    "fadeOutSec": 0,
    "clips": [
      {
        "assetId": "ast_3f9a1c7e5b2d4086a1c4",
        "title": "Interview with a founder",
        "durationSec": 1800,
        "state": "ready"
      }
    ],
    "segments": [
      {
        "id": "seg_a9dcee59c1fb9edb6751",
        "assetId": "ast_3f9a1c7e5b2d4086a1c4",
        "sourceStartSec": 302,
        "sourceEndSec": 330,
        "zoom": 1.2,
        "muted": false,
        "volume": 1,
        "outputStartSec": 0,
        "outputEndSec": 28
      },
      {
        "id": "seg_7279fbb508f4a22e65a4",
        "assetId": "ast_3f9a1c7e5b2d4086a1c4",
        "sourceStartSec": 120,
        "sourceEndSec": 150,
        "zoom": 1,
        "muted": false,
        "volume": 0.8,
        "outputStartSec": 28,
        "outputEndSec": 58
      }
    ],
    "texts": [
      {
        "id": "ovl_49f5239b9cd73496225d",
        "text": "Paid before the product",
        "preset": "headline",
        "outputStartSec": 0,
        "durationSec": 3,
        "xPercent": null,
        "yPercent": null
      }
    ],
    "broll": [
      {
        "id": "brl_f44d18e008ec4009bf10",
        "assetId": "ast_8ac5d8b97546145cdbf6",
        "outputStartSec": 12,
        "durationSec": 4,
        "fit": "cover"
      }
    ],
    "diagnostics": []
  },
  "compositionId": "cmp_cfbdd85eaaff301fa997",
  "warnings": []
}

Change the texts, the b-roll and the music

POST /v1/projects/prj_b9c1f9c7873dcef51a57/timeline/operations

Request body
{
  "expectedRevision": 1,
  "operations": [
    {
      "op": "update_text",
      "textId": "ovl_49f5239b9cd73496225d",
      "text": "They paid before we built it"
    },
    {
      "op": "add_text",
      "text": "Watch to the end",
      "preset": "caption-note",
      "outputStartSec": 50,
      "durationSec": 4
    },
    {
      "op": "update_broll",
      "brollId": "brl_f44d18e008ec4009bf10",
      "durationSec": 3
    },
    {
      "op": "add_broll",
      "assetId": "ast_8ac5d8b97546145cdbf6",
      "outputStartSec": 20,
      "durationSec": 3,
      "fit": "contain"
    },
    {
      "op": "set_music",
      "assetId": "ast_e8a35ed674148f27b585",
      "volume": 0.2
    },
    {
      "op": "set_video_fade",
      "edge": "start",
      "sec": 1
    }
  ]
}
HTTP 200
{
  "timeline": {
    "id": "tml_bbe061171a8229f8daed",
    "projectId": "prj_b9c1f9c7873dcef51a57",
    "compositionId": "cmp_cfbdd85eaaff301fa997",
    "revision": 2,
    "durationSec": 60,
    "aspect": "9:16",
    "reframe": "off",
    "captions": {
      "enabled": true,
      "style": "bold-karaoke"
    },
    "music": {
      "assetId": "ast_e8a35ed674148f27b585",
      "volume": 0.2
    },
    "fadeInSec": 1,
    "fadeOutSec": 0,
    "clips": [
      {
        "assetId": "ast_3f9a1c7e5b2d4086a1c4",
        "title": "Interview with a founder",
        "durationSec": 1800,
        "state": "ready"
      }
    ],
    "segments": [
      {
        "id": "seg_7279fbb508f4a22e65a4",
        "assetId": "ast_3f9a1c7e5b2d4086a1c4",
        "sourceStartSec": 120,
        "sourceEndSec": 150,
        "zoom": 1,
        "muted": false,
        "volume": 1,
        "outputStartSec": 0,
        "outputEndSec": 30
      },
      {
        "id": "seg_a9dcee59c1fb9edb6751",
        "assetId": "ast_3f9a1c7e5b2d4086a1c4",
        "sourceStartSec": 300,
        "sourceEndSec": 330,
        "zoom": 1,
        "muted": false,
        "volume": 1,
        "outputStartSec": 30,
        "outputEndSec": 60
      }
    ],
    "texts": [
      {
        "id": "ovl_49f5239b9cd73496225d",
        "text": "They paid before we built it",
        "preset": "headline",
        "outputStartSec": 0,
        "durationSec": 3,
        "xPercent": null,
        "yPercent": null
      },
      {
        "id": "ovl_7b2d9e04c1a6f538b0e4",
        "text": "Watch to the end",
        "preset": "caption-note",
        "outputStartSec": 50,
        "durationSec": 4,
        "xPercent": null,
        "yPercent": null
      }
    ],
    "broll": [
      {
        "id": "brl_f44d18e008ec4009bf10",
        "assetId": "ast_8ac5d8b97546145cdbf6",
        "outputStartSec": 12,
        "durationSec": 3,
        "fit": "cover"
      },
      {
        "id": "brl_1f9c4a7e2d6b08e35c91",
        "assetId": "ast_8ac5d8b97546145cdbf6",
        "outputStartSec": 20,
        "durationSec": 3,
        "fit": "contain"
      }
    ],
    "diagnostics": []
  },
  "compositionId": "cmp_cfbdd85eaaff301fa997",
  "warnings": []
}

Remove a text and a b-roll, and move a segment to the end

POST /v1/projects/prj_b9c1f9c7873dcef51a57/timeline/operations

Request body
{
  "expectedRevision": 1,
  "operations": [
    {
      "op": "remove_text",
      "textId": "ovl_49f5239b9cd73496225d"
    },
    {
      "op": "remove_broll",
      "brollId": "brl_f44d18e008ec4009bf10"
    },
    {
      "op": "move_segment",
      "segmentId": "seg_7279fbb508f4a22e65a4",
      "beforeSegmentId": null
    }
  ]
}
HTTP 200
{
  "timeline": {
    "id": "tml_bbe061171a8229f8daed",
    "projectId": "prj_b9c1f9c7873dcef51a57",
    "compositionId": "cmp_cfbdd85eaaff301fa997",
    "revision": 2,
    "durationSec": 60,
    "aspect": "9:16",
    "reframe": "off",
    "captions": {
      "enabled": true,
      "style": "bold-karaoke"
    },
    "music": null,
    "fadeInSec": 0,
    "fadeOutSec": 0,
    "clips": [
      {
        "assetId": "ast_3f9a1c7e5b2d4086a1c4",
        "title": "Interview with a founder",
        "durationSec": 1800,
        "state": "ready"
      }
    ],
    "segments": [
      {
        "id": "seg_a9dcee59c1fb9edb6751",
        "assetId": "ast_3f9a1c7e5b2d4086a1c4",
        "sourceStartSec": 300,
        "sourceEndSec": 330,
        "zoom": 1,
        "muted": false,
        "volume": 1,
        "outputStartSec": 0,
        "outputEndSec": 30
      },
      {
        "id": "seg_7279fbb508f4a22e65a4",
        "assetId": "ast_3f9a1c7e5b2d4086a1c4",
        "sourceStartSec": 120,
        "sourceEndSec": 150,
        "zoom": 1,
        "muted": false,
        "volume": 1,
        "outputStartSec": 30,
        "outputEndSec": 60
      }
    ],
    "texts": [],
    "broll": [],
    "diagnostics": []
  },
  "compositionId": "cmp_cfbdd85eaaff301fa997",
  "warnings": []
}

Change the captions, a word and the shape

POST /v1/projects/prj_b9c1f9c7873dcef51a57/timeline/operations

Request body
{
  "expectedRevision": 1,
  "operations": [
    {
      "op": "set_captions",
      "style": "word-pop",
      "options": {
        "uppercase": true,
        "maxWordsPerGroup": 3
      }
    },
    {
      "op": "replace_caption_word",
      "from": "customer",
      "to": "client"
    },
    {
      "op": "set_aspect",
      "aspect": "1:1"
    },
    {
      "op": "set_reframe",
      "mode": "center"
    }
  ]
}
HTTP 200
{
  "timeline": {
    "id": "tml_bbe061171a8229f8daed",
    "projectId": "prj_b9c1f9c7873dcef51a57",
    "compositionId": "cmp_cfbdd85eaaff301fa997",
    "revision": 2,
    "durationSec": 60,
    "aspect": "1:1",
    "reframe": "center",
    "captions": {
      "enabled": true,
      "style": "word-pop"
    },
    "music": null,
    "fadeInSec": 0,
    "fadeOutSec": 0,
    "clips": [
      {
        "assetId": "ast_3f9a1c7e5b2d4086a1c4",
        "title": "Interview with a founder",
        "durationSec": 1800,
        "state": "ready"
      }
    ],
    "segments": [
      {
        "id": "seg_7279fbb508f4a22e65a4",
        "assetId": "ast_3f9a1c7e5b2d4086a1c4",
        "sourceStartSec": 120,
        "sourceEndSec": 150,
        "zoom": 1,
        "muted": false,
        "volume": 1,
        "outputStartSec": 0,
        "outputEndSec": 30
      },
      {
        "id": "seg_a9dcee59c1fb9edb6751",
        "assetId": "ast_3f9a1c7e5b2d4086a1c4",
        "sourceStartSec": 300,
        "sourceEndSec": 330,
        "zoom": 1,
        "muted": false,
        "volume": 1,
        "outputStartSec": 30,
        "outputEndSec": 60
      }
    ],
    "texts": [
      {
        "id": "ovl_49f5239b9cd73496225d",
        "text": "Paid before the product",
        "preset": "headline",
        "outputStartSec": 0,
        "durationSec": 3,
        "xPercent": null,
        "yPercent": null
      }
    ],
    "broll": [
      {
        "id": "brl_f44d18e008ec4009bf10",
        "assetId": "ast_8ac5d8b97546145cdbf6",
        "outputStartSec": 12,
        "durationSec": 4,
        "fit": "cover"
      }
    ],
    "diagnostics": []
  },
  "compositionId": "cmp_cfbdd85eaaff301fa997",
  "warnings": []
}

The edit changed since you read it

POST /v1/projects/prj_b9c1f9c7873dcef51a57/timeline/operations

Request body
{
  "expectedRevision": 0,
  "operations": [
    {
      "op": "remove_fillers"
    }
  ]
}
HTTP 409
{
  "error": {
    "code": "CONFLICT",
    "message": "This edit changed since revision 0; its revision is now 1.",
    "hint": "Read it again with get_timeline, then send the operations with its current revision.",
    "requestId": "req_8e0b4d2a6c1f93e57a0d4b29"
  }
}

A segment that is not in the edit

POST /v1/projects/prj_b9c1f9c7873dcef51a57/timeline/operations

Request body
{
  "expectedRevision": 1,
  "operations": [
    {
      "op": "remove_segments",
      "segmentIds": [
        "seg_77e5dc932c642be5375c"
      ]
    }
  ]
}
HTTP 404
{
  "error": {
    "code": "NOT_FOUND",
    "message": "Segment seg_77e5dc932c642be5375c not found",
    "hint": "Check the id. An id that belongs to another account is not found either.",
    "requestId": "req_2c7a9e1d4f0b36e85c2a7d91"
  }
}

Errors

CodeStatusWhenWhat to do
NOT_FOUND404A segment that is not in the editChange the request, then send it again.
CONFLICT409The edit changed since you read itRead the resource again, then decide. Do not send the same request again.
NOT_READY409See the error reference.Wait for Retry-After, then send the same request again.

Every call can also answer these errors. VALIDATION_FAILED, UNKNOWN_PARAMETER, UNAUTHORIZED, SCOPE_MISSING, IDEMPOTENCY_IN_PROGRESS, PAYLOAD_TOO_LARGE, IDEMPOTENCY_KEY_REUSED, RATE_LIMITED, INTERNAL, UNAVAILABLE.

Cost

No credits are used. Adding a video costs credits, and has its own call: add_timeline_clip (POST /v1/projects/{projectId}/timeline/clips).

  • get_timeline (GET /v1/projects/{projectId}/timeline) Get an edit
  • add_timeline_clip (POST /v1/projects/{projectId}/timeline/clips) Add a video to an edit
  • list_timeline_revisions (GET /v1/projects/{projectId}/timeline/revisions) List the revisions of an edit
  • restore_timeline (POST /v1/projects/{projectId}/timeline/restore) Restore a revision of an edit
  • create_export (POST /v1/compositions/{compositionId}/exports) Export a composition

Questions

Why do I get CONFLICT with a revision number?

The edit changed after you read it: another call, the Editor Assistant or someone in the GPT-Video app wrote a new revision. Read it again with get_timeline (GET /v1/projects/{projectId}/timeline), then send your change with the revision it shows.

Why is the op "add_clip" refused?

Adding a video costs credits, so it has its own call with its own Idempotency-Key: add_timeline_clip (POST /v1/projects/{projectId}/timeline/clips). Every operation of this call is free, and an op this call does not know is refused with VALIDATION_FAILED.

What happens if one operation of the list fails?

Nothing is written: the operations apply all together or not at all, and the answer says which one failed. Fix it and send the list again with the same expectedRevision.

View as Markdown