curl -X PATCH "https://api.hooked.so/v1/project/cm4x9k2p10001ab12cd34ef56/edit" \
-H "x-api-key: your_api_key_here" \
-H "Content-Type: application/json" \
-d '{
"captionSegments": [{ "index": 1, "text": "changed my mornings forever" }],
"caption": { "preset": "beast", "alignment": "bottom" },
"music": { "musicId": "12", "volume": 0.3 },
"branding": { "logoMediaId": "cm4x9p3r40005ab12gh56ij78", "position": "top-right" }
}'
const projectId = "cm4x9k2p10001ab12cd34ef56";
const headers = {
"x-api-key": process.env.HOOKED_API_KEY,
"Content-Type": "application/json",
};
// 1. Fix a caption line and lower the music
const edit = await fetch(`https://api.hooked.so/v1/project/${projectId}/edit`, {
method: "PATCH",
headers,
body: JSON.stringify({
captionSegments: [{ index: 1, text: "changed my mornings forever" }],
music: { volume: 0.3 },
}),
});
const edited = await edit.json();
if (!edited.success) throw new Error(edited.message);
// 2. Render the edited project
const render = await fetch("https://api.hooked.so/v1/render", {
method: "POST",
headers,
body: JSON.stringify({ projectId }),
});
const { data } = await render.json();
console.log("New video:", data.videoId);
import os
import requests
project_id = "cm4x9k2p10001ab12cd34ef56"
headers = {"x-api-key": os.environ["HOOKED_API_KEY"]}
edited = requests.patch(
f"https://api.hooked.so/v1/project/{project_id}/edit",
headers=headers,
json={
"caption": {"preset": "beast"},
"branding": {"logoMediaId": "cm4x9p3r40005ab12gh56ij78", "position": "top-right"},
},
).json()
if not edited["success"]:
raise RuntimeError(edited["message"])
render = requests.post(
"https://api.hooked.so/v1/render",
headers=headers,
json={"projectId": project_id},
).json()
print("New video:", render["data"]["videoId"])
{
"success": true,
"message": "Project edited",
"data": {
"projectId": "cm4x9k2p10001ab12cd34ef56",
"changed": ["captionSegments", "caption.preset", "caption.alignment", "music.musicId", "music.volume", "branding.logoMediaId", "branding.position"],
"caption": { "disabled": false, "preset": "beast", "alignment": "bottom", "segments": 14 },
"music": { "musicId": "12", "name": "Arietta", "volume": 0.3 },
"branding": { "logoMediaId": "cm4x9p3r40005ab12gh56ij78", "position": "top-right", "size": 100 }
}
}
{
"success": false,
"message": "voiceId: Cannot be edited through the API. Only caption, captionSegments, music and branding can; edit anything else in the dashboard"
}
{
"success": false,
"message": "A video of this project is still rendering"
}
{
"success": false,
"message": "Project not found"
}
Edit Project
Change a finished project’s captions, music or logo, then render it again
curl -X PATCH "https://api.hooked.so/v1/project/cm4x9k2p10001ab12cd34ef56/edit" \
-H "x-api-key: your_api_key_here" \
-H "Content-Type: application/json" \
-d '{
"captionSegments": [{ "index": 1, "text": "changed my mornings forever" }],
"caption": { "preset": "beast", "alignment": "bottom" },
"music": { "musicId": "12", "volume": 0.3 },
"branding": { "logoMediaId": "cm4x9p3r40005ab12gh56ij78", "position": "top-right" }
}'
const projectId = "cm4x9k2p10001ab12cd34ef56";
const headers = {
"x-api-key": process.env.HOOKED_API_KEY,
"Content-Type": "application/json",
};
// 1. Fix a caption line and lower the music
const edit = await fetch(`https://api.hooked.so/v1/project/${projectId}/edit`, {
method: "PATCH",
headers,
body: JSON.stringify({
captionSegments: [{ index: 1, text: "changed my mornings forever" }],
music: { volume: 0.3 },
}),
});
const edited = await edit.json();
if (!edited.success) throw new Error(edited.message);
// 2. Render the edited project
const render = await fetch("https://api.hooked.so/v1/render", {
method: "POST",
headers,
body: JSON.stringify({ projectId }),
});
const { data } = await render.json();
console.log("New video:", data.videoId);
import os
import requests
project_id = "cm4x9k2p10001ab12cd34ef56"
headers = {"x-api-key": os.environ["HOOKED_API_KEY"]}
edited = requests.patch(
f"https://api.hooked.so/v1/project/{project_id}/edit",
headers=headers,
json={
"caption": {"preset": "beast"},
"branding": {"logoMediaId": "cm4x9p3r40005ab12gh56ij78", "position": "top-right"},
},
).json()
if not edited["success"]:
raise RuntimeError(edited["message"])
render = requests.post(
"https://api.hooked.so/v1/render",
headers=headers,
json={"projectId": project_id},
).json()
print("New video:", render["data"]["videoId"])
{
"success": true,
"message": "Project edited",
"data": {
"projectId": "cm4x9k2p10001ab12cd34ef56",
"changed": ["captionSegments", "caption.preset", "caption.alignment", "music.musicId", "music.volume", "branding.logoMediaId", "branding.position"],
"caption": { "disabled": false, "preset": "beast", "alignment": "bottom", "segments": 14 },
"music": { "musicId": "12", "name": "Arietta", "volume": 0.3 },
"branding": { "logoMediaId": "cm4x9p3r40005ab12gh56ij78", "position": "top-right", "size": 100 }
}
}
{
"success": false,
"message": "voiceId: Cannot be edited through the API. Only caption, captionSegments, music and branding can; edit anything else in the dashboard"
}
{
"success": false,
"message": "A video of this project is still rendering"
}
{
"success": false,
"message": "Project not found"
}
Overview
Makes a few targeted changes to a finished project. Each one is saved to the project’s editor timeline the same way the Hooked editor saves it, so the project opens in the dashboard with your changes. What you can edit through the API:| Field | What it changes |
|---|---|
caption.preset | The caption style, by an id of caption-presets. It brings the style’s look, box size and entrance animation, and re-cuts the lines to fit |
caption.alignment | Where the captions sit: top, middle or bottom |
caption.disabled | true hides the captions in the render, false shows them again. Nothing is deleted |
captionSegments | New text for caption lines, by the index Get Project Captions answered |
music.musicId | Another background track from List Music, or null to remove the music |
music.volume | Music volume, 0 to 1 |
branding.logoMediaId | An image of your media library as the logo, or null to remove it |
branding.position, branding.size | Move the logo to a corner, or resize it (20 to 200 px) |
400 that names it, and nothing is saved.
Editing is free and does not render anything. When you are done, call Render Project to get a new video.
How each edit is applied
- Caption text. The line keeps its start and end time; its words share that time evenly, as when you retype a line in the editor. Read the lines first with Get Project Captions:
indexcounts from0across the whole video. - Caption style. A project made from a template has one caption layer per scene; the style, alignment and switch apply to all of them. A new style re-cuts the lines to its box, so line numbers change: read the captions again before correcting text. In one request,
captionSegmentsis applied first, with the indexes you read. - Music. The new track takes the old one’s place and timing, and plays from its start. A project without music gets the track from start to end at volume
0.5. A track shorter than the video stops when it ends. The song of a Music to Video project cannot be swapped (its volume can). - Logo. The new image replaces the logo where it is. Without a logo yet,
logoMediaIdadds one over the whole video,bottom-rightat 100 px unless you say otherwise. The image must beCOMPLETEDin your library.
When it is refused
| Status | message | What to do |
|---|---|---|
409 | Project is still processing | Wait until the project is completed or failed (Get Project). |
409 | A video of this project is still rendering | Wait until its video.status is COMPLETED or FAILED. |
409 | The project changed while it was being edited… | Someone saved the project (in the dashboard) during your request. Read it again and retry. |
400 | There is nothing on the timeline to edit yet | The project has not finished generating. |
400 | caption: The project has no captions | Captions cannot be added through the API. |
curl -X PATCH "https://api.hooked.so/v1/project/cm4x9k2p10001ab12cd34ef56/edit" \
-H "x-api-key: your_api_key_here" \
-H "Content-Type: application/json" \
-d '{
"captionSegments": [{ "index": 1, "text": "changed my mornings forever" }],
"caption": { "preset": "beast", "alignment": "bottom" },
"music": { "musicId": "12", "volume": 0.3 },
"branding": { "logoMediaId": "cm4x9p3r40005ab12gh56ij78", "position": "top-right" }
}'
const projectId = "cm4x9k2p10001ab12cd34ef56";
const headers = {
"x-api-key": process.env.HOOKED_API_KEY,
"Content-Type": "application/json",
};
// 1. Fix a caption line and lower the music
const edit = await fetch(`https://api.hooked.so/v1/project/${projectId}/edit`, {
method: "PATCH",
headers,
body: JSON.stringify({
captionSegments: [{ index: 1, text: "changed my mornings forever" }],
music: { volume: 0.3 },
}),
});
const edited = await edit.json();
if (!edited.success) throw new Error(edited.message);
// 2. Render the edited project
const render = await fetch("https://api.hooked.so/v1/render", {
method: "POST",
headers,
body: JSON.stringify({ projectId }),
});
const { data } = await render.json();
console.log("New video:", data.videoId);
import os
import requests
project_id = "cm4x9k2p10001ab12cd34ef56"
headers = {"x-api-key": os.environ["HOOKED_API_KEY"]}
edited = requests.patch(
f"https://api.hooked.so/v1/project/{project_id}/edit",
headers=headers,
json={
"caption": {"preset": "beast"},
"branding": {"logoMediaId": "cm4x9p3r40005ab12gh56ij78", "position": "top-right"},
},
).json()
if not edited["success"]:
raise RuntimeError(edited["message"])
render = requests.post(
"https://api.hooked.so/v1/render",
headers=headers,
json={"projectId": project_id},
).json()
print("New video:", render["data"]["videoId"])
{
"success": true,
"message": "Project edited",
"data": {
"projectId": "cm4x9k2p10001ab12cd34ef56",
"changed": ["captionSegments", "caption.preset", "caption.alignment", "music.musicId", "music.volume", "branding.logoMediaId", "branding.position"],
"caption": { "disabled": false, "preset": "beast", "alignment": "bottom", "segments": 14 },
"music": { "musicId": "12", "name": "Arietta", "volume": 0.3 },
"branding": { "logoMediaId": "cm4x9p3r40005ab12gh56ij78", "position": "top-right", "size": 100 }
}
}
{
"success": false,
"message": "voiceId: Cannot be edited through the API. Only caption, captionSegments, music and branding can; edit anything else in the dashboard"
}
{
"success": false,
"message": "A video of this project is still rendering"
}
{
"success": false,
"message": "Project not found"
}
Response
data shows the editable parts of the project after the edit: caption, music and branding are null when the project has none. changed lists what this request changed. branding.logoMediaId is null for a logo added when the project was created.
Errors
| Status | When |
|---|---|
| 400 | Invalid edit, as <field>: <reason>: an unknown field, an unknown caption style, a segment index out of range, a music or logo id that is not yours, an image still importing. Nothing was saved |
| 401 | Missing or invalid API key (code: "not_authenticated") |
| 403 | The team does not have the Hooked app product (code: "entitlement_required") |
| 404 | No such project. Another team’s project, or an id that is not one, answers the same Project not found |
| 409 | The project is generating, one of its videos is rendering, or it was saved elsewhere during the request. Nothing was saved |
| 500 | Internal server error. Read the captions or the project before retrying |
Authorizations
Path Parameters
The projectId a create endpoint answered, or one from GET /v1/project/list.
Body
Send at least one of the four groups. A field that is not listed here is refused with a 400: edit anything else in the dashboard.
The captions' style, position and on/off switch. Applies to every caption layer of the project (a template project has one per scene).
Show child attributes
Show child attributes
New text for caption lines, by the index Get Project Captions answered (each index once). The line keeps its start and end; its words share that time evenly, as when you retype a line in the editor. Applied before caption.preset, so the indexes are the ones you read.
1 - 500 elementsShow child attributes
Show child attributes
The background music. Not available for the song of a Music to Video project.
Show child attributes
Show child attributes
The logo over the video (the branding logo a create request can add). Without a logo yet, logoMediaId adds one over the whole video.
Show child attributes
Show child attributes