Frame a composition again
reframe_compositionPOST/v1/compositions/{compositionId}/reframeBeta
Computes the vertical framing of a composition again, following the face or keeping the center.
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
It starts a framing job and answers 202 with it; follow it with get_job (GET /v1/jobs/{jobId}), or read framing in get_composition (GET /v1/compositions/{compositionId}). face follows the face of the speaker, center keeps the middle of the video. When a framing job is already running on the composition, the answer is that job with reused true, and nothing new starts. Works on Auto Clip and streamer compositions; a Vibe Editing project changes its framing with the set_reframe operation of apply_timeline_operations (POST /v1/projects/{projectId}/timeline/operations). An export started while the framing runs answers NOT_READY.
Example use
A clip was framed on the center, and the speaker sits at the edge of the picture. A script asks for the face framing, follows the job with get_job (GET /v1/jobs/{jobId}), and exports the clip again when the job is done.
Endpoint
POST https://api.gpt-video.com/v1/compositions/{compositionId}/reframe- Scope
clips:write. - Rate limit 60 requests a minute for each key and 120 for each account, shared by every
writecall. See rate limits. Idempotency-Keyis optional.- It starts work that runs on its own. The answer comes back before the work is done.
Parameters
Path
| Name | Type | Required | Description |
|---|---|---|---|
compositionId | string | Required | The id of the composition: accept_clip (POST /v1/clips/{clipId}/accept) returns it for a clip, and list_compositions (GET /v1/projects/{projectId}/compositions) or the project's compositionId give it for the other kinds of project. Matches ^cmp_[0-9a-f]{20}$. |
Headers
| Name | Type | Required | Description |
|---|---|---|---|
Idempotency-Key | string | Optional | Send the same key on every retry of this request. From 1 to 255 visible characters, such as a UUID. See idempotency. |
Body
| Name | Type | Required | Description |
|---|---|---|---|
mode | string | Required | face follows the face of the speaker, center keeps the middle of the video. One of face, center. |
Send only the fields listed here. Any other field is refused with UNKNOWN_PARAMETER.
Request
Follow the face.
curl -X POST "https://api.gpt-video.com/v1/compositions/cmp_4e8c2a6f09d14b73a1e5/reframe" \
-H "Authorization: Bearer $GPT_VIDEO_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"mode": "face"
}'const res = await fetch("https://api.gpt-video.com/v1/compositions/cmp_4e8c2a6f09d14b73a1e5/reframe", {
method: "POST",
headers: {
"Authorization": `Bearer ${process.env.GPT_VIDEO_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
"mode": "face"
}),
});
const data = await res.json();
console.log(data);import os
import requests
r = requests.post(
"https://api.gpt-video.com/v1/compositions/cmp_4e8c2a6f09d14b73a1e5/reframe",
headers={
"Authorization": f"Bearer {os.environ['GPT_VIDEO_API_KEY']}",
},
json={
"mode": "face"
},
)
data = r.json()
print(data){
"method": "tools/call",
"params": {
"name": "reframe_composition",
"arguments": {
"compositionId": "cmp_4e8c2a6f09d14b73a1e5",
"mode": "face"
}
}
}Response
A successful call answers HTTP 202. Returns a Job object in job.
Fields to check
| Name | Type | Description |
|---|---|---|
job.id | string | The job id. It starts with job_. |
job.status | string | queued and running are still working; done, failed and cancelled are final. |
reused | boolean | true when a framing job was already running on the composition and this is that job. |
Response example
Location: https://api.gpt-video.com/v1/jobs/job_4e8a2c6f0b3d49a7e1c5
{
"job": {
"id": "job_4e8a2c6f0b3d49a7e1c5",
"kind": "framing",
"status": "queued",
"progress": 0,
"errorMessage": null,
"projectId": "prj_7c2e9a4f1b3d4e8096a5",
"compositionId": "cmp_4e8c2a6f09d14b73a1e5",
"exportId": null,
"assetId": null,
"toolRunId": null,
"turnId": null,
"createdAt": "2026-09-01T11:10:00.000Z",
"startedAt": null,
"finishedAt": null
},
"reused": false
}All response fields
| Name | Type | Required | Description |
|---|---|---|---|
job | Job object | Required | Described in Job object. |
reused | boolean | Required | true when a framing job was already running on the composition and this is that job. |
More examples
A framing that is already running
POST /v1/compositions/cmp_4e8c2a6f09d14b73a1e5/reframe
{
"mode": "center"
}Location: https://api.gpt-video.com/v1/jobs/job_4e8a2c6f0b3d49a7e1c5
{
"job": {
"id": "job_4e8a2c6f0b3d49a7e1c5",
"kind": "framing",
"status": "queued",
"progress": 0,
"errorMessage": null,
"projectId": "prj_7c2e9a4f1b3d4e8096a5",
"compositionId": "cmp_4e8c2a6f09d14b73a1e5",
"exportId": null,
"assetId": null,
"toolRunId": null,
"turnId": null,
"createdAt": "2026-09-01T11:10:00.000Z",
"startedAt": null,
"finishedAt": null
},
"reused": true
}A composition of a Vibe Editing project
POST /v1/compositions/cmp_cfbdd85eaaff301fa997/reframe
{
"mode": "face"
}{
"error": {
"code": "CONFLICT",
"message": "Only Auto Clip and streamer compositions follow a face. For a Vibe Editing project use the set_reframe operation.",
"requestId": "req_2b7d5f9a1c3e46b0d8a2c4e6"
}
}Frame a composition that does not exist
POST /v1/compositions/cmp_7a1f5d3c9e0b42d8f6a2/reframe
{
"mode": "face"
}{
"error": {
"code": "NOT_FOUND",
"message": "Composition cmp_7a1f5d3c9e0b42d8f6a2 not found",
"hint": "Check the id. An id that belongs to another account is not found either.",
"requestId": "req_6b2d9f0e4a7c41b3d8e5a2f9"
}
}Errors
| Code | Status | When | What to do |
|---|---|---|---|
NOT_FOUND | 404 | Frame a composition that does not exist | Change the request, then send it again. |
CONFLICT | 409 | A composition of a Vibe Editing project | Read the resource again, then decide. Do not 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.
Related operations
get_job(GET /v1/jobs/{jobId}) Get a jobget_composition(GET /v1/compositions/{compositionId}) Get a compositioncreate_export(POST /v1/compositions/{compositionId}/exports) Export a compositionedit_composition(POST /v1/compositions/{compositionId}/edit) Edit a composition
Questions
Does a trim keep the framing?
No. A trim made with edit_composition (POST /v1/compositions/{compositionId}/edit) clears the face framing, because it was computed for the old start; when the project follows the face, a framing job starts again by itself, and get_composition (GET /v1/compositions/{compositionId}) shows it as tracking.
Why did I get a job that was started earlier?
A framing job was already running on the composition, so the answer is that job with reused true. Wait for it with get_job (GET /v1/jobs/{jobId}), then ask again if you want the other mode.