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.

Send a message to the Editor Assistant

create_assistant_turnPOST/v1/projects/{projectId}/assistant/turnsBeta

Asks the Editor Assistant to change a project in plain words, and returns the turn that does it.

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

Call this to ask the Editor Assistant to edit a project, for example "cut the pauses longer than one second" or "make the captions bolder". The call answers 202 at once with the turn, queued, and a Location to follow; read it with get_assistant_turn (GET /v1/assistant/turns/{turnId}) (with ?wait= to hold the request) until it is done, then read its reply and its actions. The conversation is the same one the project shows in the GPT-Video app, where this message is marked as sent through the API. A turn through the API never spends credits or exports by itself: when the request needs an analysis, a build, a new clip in an edit or an export, the reply names the API call to make, which you send with its own Idempotency-Key. It runs at the Fast level, and it does not save notes. While a turn is running on the project, another turn sent through the API is running in the account, or a message written in the GPT-Video app is waiting for its answer, the call answers NOT_READY: wait for it, then send your message again. When the Editor Assistant is paused, the call answers UNAVAILABLE and uses no turn of the day. An Editor Assistant turn can change the project, so the key needs the clips:write scope as well as spend. Always send an Idempotency-Key, the same one on every retry: a retry of the same request returns the same turn and never uses a second turn of the day.

Example use

A support tool lets a client type a request such as "make the captions bolder" for their project. The tool sends it here with an Idempotency-Key, then follows the turn with get_assistant_turn (GET /v1/assistant/turns/{turnId}) until the reply arrives.

Endpoint

Code
POST https://api.gpt-video.com/v1/projects/{projectId}/assistant/turns
  • Scope spend. The key needs all of spend and clips:write.
  • Rate limit 10 requests a minute for each key and 20 for each account, shared by every spend call. See rate limits.
  • Idempotency-Key is required.
  • It starts work that runs on its own. The answer comes back before the work is done.

Parameters

Path

NameTypeRequiredDescription
projectIdstringRequiredThe id of the project the Editor Assistant works on. Matches ^prj_[0-9a-f]{20}$.

Headers

NameTypeRequiredDescription
Idempotency-KeystringRequiredThis operation spends, so the key is required. Create it once, before the request, and send the same key on every retry. From 1 to 255 visible characters, such as a UUID. See idempotency.

Body

NameTypeRequiredDescription
messagestringRequiredWhat you want done, in plain words, up to 4000 characters. From 1 to 4000 characters.

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

Request

Ask for a change to an edit.

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/assistant/turns" \
  -H "Authorization: Bearer $GPT_VIDEO_API_KEY" \
  -H "Idempotency-Key: $IDEMPOTENCY_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "message": "Cut the pauses longer than one second."
  }'
// 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/assistant/turns", {
  method: "POST",
  headers: {
    "Authorization": `Bearer ${process.env.GPT_VIDEO_API_KEY}`,
    "Idempotency-Key": idempotencyKey,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    "message": "Cut the pauses longer than one second."
  }),
});
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/assistant/turns",
    headers={
        "Authorization": f"Bearer {os.environ['GPT_VIDEO_API_KEY']}",
        "Idempotency-Key": idempotency_key,
    },
    json={
        "message": "Cut the pauses longer than one second."
    },
)
data = r.json()
print(data)
{
  "method": "tools/call",
  "params": {
    "name": "create_assistant_turn",
    "arguments": {
      "projectId": "prj_b9c1f9c7873dcef51a57",
      "message": "Cut the pauses longer than one second.",
      "confirm": true,
      "idempotencyKey": "REPLACE-WITH-A-NEW-UUID-FOR-THIS-CALL"
    }
  }
}

Response

A successful call answers HTTP 202. Returns a Assistant turn object in turn.

Fields to check

NameTypeDescription
turn.idstringThe turn id. It starts with agm_.
turn.statusstringqueued and running are still working; done, failed and cancelled are final.
turn.actions[].idstringThe action id. It starts with tc_.
turn.actions[].statusstring
turn.jobIdstring or nullThe job that runs the turn; null once it is gone.
paidWithstring or nullWho paid for this request: plan is the credits or the daily allowance of the account's plan, api_bonus is the API bonus. null when this request paid nothing, because the same work was already running. A retry with the same Idempotency-Key says who paid the first time.

Response example

  • Location: https://api.gpt-video.com/v1/assistant/turns/agm_7c2e9a4f1b6d38e05a2c
HTTP 202
{
  "turn": {
    "id": "agm_7c2e9a4f1b6d38e05a2c",
    "projectId": "prj_b9c1f9c7873dcef51a57",
    "status": "queued",
    "message": "Cut the pauses longer than one second.",
    "reply": null,
    "outcome": null,
    "question": null,
    "options": [],
    "actions": [],
    "jobId": "job_3e8b1d6f9a2c40e75b1d",
    "errorMessage": null,
    "createdAt": "2026-09-01T11:00:00.000Z",
    "finishedAt": null
  },
  "paidWith": "plan"
}
All response fields
NameTypeRequiredDescription
turnAssistant turn objectRequiredDescribed in Assistant turn object.
paidWithstring or nullRequiredWho paid for this request: plan is the credits or the daily allowance of the account's plan, api_bonus is the API bonus. null when this request paid nothing, because the same work was already running. A retry with the same Idempotency-Key says who paid the first time. One of plan, api_bonus.
More examples

A turn is still running on the project

POST /v1/projects/prj_b9c1f9c7873dcef51a57/assistant/turns

Request body
{
  "message": "And make the text overlays shorter."
}
  • Retry-After: 10
HTTP 409
{
  "error": {
    "code": "NOT_READY",
    "message": "A turn of the Editor Assistant is already running on this project. Wait for it with get_assistant_turn, then send your message again.",
    "hint": "Wait the number of seconds in the Retry-After header, then send the same request again.",
    "requestId": "req_8b3e1f7a2c6d4e9a0f5b2c71"
  }
}

Another turn sent through the API is still running

POST /v1/projects/prj_b9c1f9c7873dcef51a57/assistant/turns

Request body
{
  "message": "Cut the pauses longer than one second."
}
  • Retry-After: 10
HTTP 409
{
  "error": {
    "code": "NOT_READY",
    "message": "Another Editor Assistant turn started through the API is still running. Wait for it to finish, then send your message again.",
    "hint": "Wait the number of seconds in the Retry-After header, then send the same request again.",
    "requestId": "req_2d7f4a9c1e6b3d8f0a5c7e42"
  }
}

A message written in the app is waiting

POST /v1/projects/prj_b9c1f9c7873dcef51a57/assistant/turns

Request body
{
  "message": "Cut the pauses longer than one second."
}
  • Retry-After: 10
HTTP 409
{
  "error": {
    "code": "NOT_READY",
    "message": "A message written in the GPT-Video app is waiting for the Editor Assistant to answer it. Wait for that answer, then send your message again.",
    "hint": "Wait the number of seconds in the Retry-After header, then send the same request again.",
    "requestId": "req_fbc30cb25d9763c7cba21957"
  }
}

The Editor Assistant is paused

POST /v1/projects/prj_b9c1f9c7873dcef51a57/assistant/turns

Request body
{
  "message": "Cut the pauses longer than one second."
}
  • Retry-After: 60
HTTP 503
{
  "error": {
    "code": "UNAVAILABLE",
    "message": "GPT-Video cannot take new work right now. Try again in a few minutes.",
    "hint": "Retry later.",
    "requestId": "req_977388db082429a89d2b5f25"
  }
}

A project that does not exist

POST /v1/projects/prj_9f0d3b7e1c5a4d28b6f3/assistant/turns

Request body
{
  "message": "Cut the pauses longer than one second."
}
HTTP 404
{
  "error": {
    "code": "NOT_FOUND",
    "message": "Project prj_9f0d3b7e1c5a4d28b6f3 not found",
    "hint": "Check the id. An id that belongs to another account is not found either.",
    "requestId": "req_6f1c9e3a7b2d4f8e0c5a9b17"
  }
}

Errors

CodeStatusWhenWhat to do
INSUFFICIENT_CREDITS402See the error reference.Stop and tell the user.
PLAN_REQUIRED402See the error reference.Stop and tell the user.
NOT_FOUND404A project that does not existChange the request, then send it again.
NOT_READY409A turn is still running on the projectWait for Retry-After, then send the same request again.
DAILY_LIMIT_REACHED429See the error reference.Stop until the limit resets at 00:00 UTC.

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

Cost

Uses one Editor Assistant turn of the plan's daily allowance, which resets at 00:00 UTC; the Creator and Studio plans allow part of their turns through the API. When the API bonus pays, it also uses two bonus credits. Nothing is returned if the turn fails. A turn through the API never spends credits or exports on its own: the actions that would, answer with the API call to use.

  • Paid by the API bonus, it uses 2 bonus credits.
  • When the API bonus pays, an account can take 20 Editor Assistant turns a day.
  • See API pricing for who pays first.
  • get_assistant_turn (GET /v1/assistant/turns/{turnId}) Get an Editor Assistant turn
  • list_assistant_turns (GET /v1/projects/{projectId}/assistant/turns) List the Editor Assistant turns of a project
  • undo_assistant_action (POST /v1/assistant/turns/{turnId}/actions/{actionId}/undo) Undo an action of the Editor Assistant
  • get_job (GET /v1/jobs/{jobId}) Get a job

Questions

Why does the reply tell me to call another operation?

A turn through the API never spends credits or exports by itself. When your request needs an analysis, a build, a new clip in an edit or an export, the reply names the call that does it, for example create_export (POST /v1/compositions/{compositionId}/exports). Send that call with its own Idempotency-Key.

What happens if my request times out and I send it again?

Send it again with the same Idempotency-Key. You get the same turn back and no second turn of the day is used. A new key sends a new message.

Can I send a message while the Editor Assistant is working?

No. While a turn runs on the project the call answers NOT_READY, and nothing is used. Wait for the turn with get_assistant_turn (GET /v1/assistant/turns/{turnId}), then send your message again.

Why does my key get SCOPE_MISSING?

A turn spends a turn of the day and changes the project, so the key needs both the spend and the clips:write scopes.

View as Markdown