Editing
Slicer API editing endpoints — video text with word ids, operations, the edit document, captions from speech, AI assistant, translation, style, own director.
On this page
- The video as text with word ids
- Apply edit operations
- Captions from speech
- Speech recognition jobs
- Command for the AI assistant
- Translate captions
- Auto colour and colour matching (free)
- Change the style
- Your own director
- Director brief for your AI assistant (free)
- Frames of a source (free)
- Apply a director plan (free)
- Edit document
- Edit document
- Replace the edit document (advanced)
- Schemas
Edits change the video the same way the app's editor does and are saved immediately. All of them are free except captions
from speech, the AI assistant and caption translation — those are priced at AI cost (Captions from
speech, AI assistant). Every edit
passes the revision rev: if the video has changed meanwhile, the response is 409 stale_document. How to use them —
in the guide Editing the video.
The video as text with word ids
/v1/projects/{id}/transcriptPath parameters
| Field | Type | Description |
|---|---|---|
idrequired | integer | ≥ 1 |
Request
curl "https://api.slicer.media/v1/projects/42/transcript" \
-H "Authorization: Bearer $SLICER_TOKEN"const response = await fetch('https://api.slicer.media/v1/projects/42/transcript', {
headers: {
Authorization: `Bearer ${process.env.SLICER_TOKEN}`,
},
});
const { ok, data, error } = await response.json();import os
import requests
response = requests.get(
"https://api.slicer.media/v1/projects/42/transcript",
headers={"Authorization": f"Bearer {os.environ['SLICER_TOKEN']}"},
)
body = response.json()Response
Response fields: Transcript (90)
| Field | Type | Description |
|---|---|---|
revrequired | integer | Document revision: pass it to edits |
durationSecrequired | number | |
textrequired | string | The video as text for an LLM: fragments with word ids w<id>, cut words in [brackets], graphics |
blocksrequired | object[] | |
blocks[].idrequired | string | |
blocks[].positionrequired | integer | |
blocks[].sourceIndexrequired | integer | |
blocks[].kindrequired | string | words · range |
blocks[].wordsrequired | object[] | |
blocks[].words[].idrequired | integer | |
blocks[].words[].textrequired | string | |
blocks[].words[].cutrequired | boolean | |
blocks[].words[].accentrequired | boolean | |
elementsrequired | object | |
elements.hookrequired | string | null | |
elements.plaquesrequired | object[] | |
elements.plaques[].idrequired | string | |
elements.plaques[].kindrequired | string | |
elements.plaques[].titlerequired | string | |
elements.plaques[].fromWordIdrequired | integer | null | Word id; null — the element is placed by time (anchor) |
elements.plaques[].toWordIdrequired | integer | null | Word id; null — the element is placed by time (anchor) |
elements.plaques[].start | number | Where the element starts in the video, s; absent — hidden (its words were cut) |
elements.plaques[].end | number | Where the element ends in the video, s |
elements.plaques[].anchor | object | Placed by time: fragment, second of its source and duration in the video, s |
elements.plaques[].anchor.blockIdrequired | string | |
elements.plaques[].anchor.atrequired | number | |
elements.plaques[].anchor.durationrequired | number | |
elements.cardsrequired | object[] | |
elements.cards[].idrequired | string | |
elements.cards[].captionrequired | string | null | |
elements.cards[].fromWordIdrequired | integer | null | Word id; null — the element is placed by time (anchor) |
elements.cards[].toWordIdrequired | integer | null | Word id; null — the element is placed by time (anchor) |
elements.cards[].start | number | Where the element starts in the video, s; absent — hidden (its words were cut) |
elements.cards[].end | number | Where the element ends in the video, s |
elements.cards[].anchor | object | Placed by time: fragment, second of its source and duration in the video, s |
elements.cards[].anchor.blockIdrequired | string | |
elements.cards[].anchor.atrequired | number | |
elements.cards[].anchor.durationrequired | number | |
elements.emojirequired | object[] | |
elements.emoji[].idrequired | string | |
elements.emoji[].emojirequired | string | |
elements.emoji[].wordIdrequired | integer | null | Word id; null — the element is placed by time (anchor) |
elements.emoji[].start | number | Where the element starts in the video, s; absent — hidden (its words were cut) |
elements.emoji[].end | number | Where the element ends in the video, s |
elements.emoji[].anchor | object | Placed by time: fragment, second of its source and duration in the video, s |
elements.emoji[].anchor.blockIdrequired | string | |
elements.emoji[].anchor.atrequired | number | |
elements.emoji[].anchor.durationrequired | number | |
elements.zoomsrequired | object[] | |
elements.zooms[].idrequired | string | |
elements.zooms[].fromWordIdrequired | integer | null | Word id; null — the element is placed by time (anchor) |
elements.zooms[].toWordIdrequired | integer | null | Word id; null — the element is placed by time (anchor) |
elements.zooms[].start | number | Where the element starts in the video, s; absent — hidden (its words were cut) |
elements.zooms[].end | number | Where the element ends in the video, s |
elements.zooms[].anchor | object | Placed by time: fragment, second of its source and duration in the video, s |
elements.zooms[].anchor.blockIdrequired | string | |
elements.zooms[].anchor.atrequired | number | |
elements.zooms[].anchor.durationrequired | number | |
elements.titlesrequired | object[] | Free text layers (titles) |
elements.titles[].idrequired | string | |
elements.titles[].textrequired | string | |
elements.titles[].fontrequired | string | |
elements.titles[].sizerequired | number | |
elements.titles[].colorrequired | string | |
elements.titles[].backgroundrequired | string | none · box · pill |
elements.titles[].backgroundColor | string | |
elements.titles[].outline | boolean | |
elements.titles[].shadow | boolean | |
elements.titles[].alignrequired | string | left · center · right |
elements.titles[].xrequired | number | |
elements.titles[].yrequired | number | |
elements.titles[].animIn | string | fade · slide · pop · typewriter |
elements.titles[].animOut | string | fade · slide · pop |
elements.titles[].fromWordIdrequired | integer | null | Word id; null — the element is placed by time (anchor) |
elements.titles[].toWordIdrequired | integer | null | Word id; null — the element is placed by time (anchor) |
elements.titles[].start | number | Where the element starts in the video, s; absent — hidden (its words were cut) |
elements.titles[].end | number | Where the element ends in the video, s |
elements.titles[].anchor | object | Placed by time: fragment, second of its source and duration in the video, s |
elements.titles[].anchor.blockIdrequired | string | |
elements.titles[].anchor.atrequired | number | |
elements.titles[].anchor.durationrequired | number | |
elements.markersrequired | object[] | Author markers on the timeline (not shown in the video); hidden markers are omitted |
elements.markers[].idrequired | string | |
elements.markers[].timerequired | number | |
elements.markers[].labelrequired | string | null | |
elements.markers[].colorrequired | string | cyan · yellow · red · green · violet |
elements.soundsrequired | object[] | Sound effects placed by the author; cut-off ones are omitted |
elements.sounds[].idrequired | string | |
elements.sounds[].kindrequired | string | whoosh · pop · ding · click · swipe · rise · boom · chime |
elements.sounds[].timerequired | number |
{
"ok": true,
"data": {
"rev": 7,
"durationSec": 42.5,
"text": "string",
"blocks": [
{
"id": "string",
"position": 1,
"sourceIndex": 1,
"kind": "words",
"words": [
{
"id": null,
"text": null,
"cut": null,
"accent": null
}
]
}
],
"elements": {
"hook": "string",
"plaques": [
{
"id": "string",
"kind": "string",
"title": "Монтаж за 3 минуты",
"fromWordId": null,
"toWordId": null,
"start": 1.5,
"end": 1.5,
"anchor": {
"blockId": null,
"at": null,
"duration": null
}
}
],
"cards": [
{
"id": "string",
"caption": "string",
"fromWordId": null,
"toWordId": null,
"start": 1.5,
"end": 1.5,
"anchor": {
"blockId": null,
"at": null,
"duration": null
}
}
],
"emoji": [
{
"id": "string",
"emoji": "string",
"wordId": null,
"start": 1.5,
"end": 1.5,
"anchor": {
"blockId": null,
"at": null,
"duration": null
}
}
],
"zooms": [
{
"id": "string",
"fromWordId": null,
"toWordId": null,
"start": 1.5,
"end": 1.5,
"anchor": {
"blockId": null,
"at": null,
"duration": null
}
}
],
"titles": [
{
"id": "string",
"text": "string",
"font": "string",
"size": 104857600,
"color": "string",
"background": "none",
"backgroundColor": "string",
"outline": true,
"shadow": true,
"align": "left",
"x": 1.5,
"y": 1.5,
"animIn": "fade",
"animOut": "fade",
"fromWordId": null,
"toWordId": null,
"start": 1.5,
"end": 1.5,
"anchor": {
"blockId": null,
"at": null,
"duration": null
}
}
],
"markers": [
{
"id": "string",
"time": 1.5,
"label": "string",
"color": "cyan"
}
],
"sounds": [
{
"id": "string",
"kind": "whoosh",
"time": 1.5
}
]
}
}
}Apply edit operations
/v1/projects/{id}/editsFree. Word ids — from GET …/transcript. Inapplicable operations are skipped and listed in skipped.
Path parameters
| Field | Type | Description |
|---|---|---|
idrequired | integer | ≥ 1 |
Request body EditsRequest
| Field | Type | Description |
|---|---|---|
revrequired | integer | Revision from GET …/transcript or …/document |
operationsrequired | EditOperation[] | 1–80 |
Request
curl -X POST "https://api.slicer.media/v1/projects/42/edits" \
-H "Authorization: Bearer $SLICER_TOKEN" \
-H "Content-Type: application/json" \
-d '{"rev":7,"operations":[{"op":"cut_words","fromWordId":42,"toWordId":42}]}'const response = await fetch('https://api.slicer.media/v1/projects/42/edits', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.SLICER_TOKEN}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
"rev": 7,
"operations": [
{
"op": "cut_words",
"fromWordId": 42,
"toWordId": 42
}
]
}),
});
const { ok, data, error } = await response.json();import os
import requests
response = requests.post(
"https://api.slicer.media/v1/projects/42/edits",
headers={"Authorization": f"Bearer {os.environ['SLICER_TOKEN']}"},
json={
"rev": 7,
"operations": [{
"op": "cut_words",
"fromWordId": 42,
"toWordId": 42,
}],
},
)
body = response.json()Response
Response fields: EditsResult (7)
| Field | Type | Description |
|---|---|---|
revrequired | integer | |
appliedrequired | integer | |
skippedrequired | object[] | |
skipped[].indexrequired | integer | |
skipped[].oprequired | string | |
skipped[].reasonrequired | string | |
durationSecrequired | number |
{
"ok": true,
"data": {
"rev": 7,
"applied": 1,
"skipped": [
{
"index": 1,
"op": "string",
"reason": "string"
}
],
"durationSec": 42.5
}
}Captions from speech
/v1/projects/{id}/transcribeRecognises speech in the project sources (a manual project or new sources) — at AI cost, per minute of sound. Without confirmCost returns the price. Runs in the background: follow it with GET …/transcribe; when ready, the words are in the transcript and captions follow the speech.
Path parameters
| Field | Type | Description |
|---|---|---|
idrequired | integer | ≥ 1 |
Request body TranscribeRequest
| Field | Type | Description |
|---|---|---|
sources | integer[] | Source indexes (default — every source with sound whose speech is not recognised yet)1–40 |
language | string | Speech language: auto or a two-letter code |
confirmCost | integer | Price you agree to pay. Without it the response is a Quote and nothing starts |
Request
curl -X POST "https://api.slicer.media/v1/projects/42/transcribe" \
-H "Authorization: Bearer $SLICER_TOKEN" \
-H "Content-Type: application/json" \
-d '{"sources":[1],"language":"ru"}'const response = await fetch('https://api.slicer.media/v1/projects/42/transcribe', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.SLICER_TOKEN}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
"sources": [
1
],
"language": "ru"
}),
});
const { ok, data, error } = await response.json();import os
import requests
response = requests.post(
"https://api.slicer.media/v1/projects/42/transcribe",
headers={"Authorization": f"Bearer {os.environ['SLICER_TOKEN']}"},
json={
"sources": [1],
"language": "ru",
},
)
body = response.json()Response
One of: Quote, TranscribeJob
Response fields: Quote (8)
| Field | Type | Description |
|---|---|---|
statusrequired | "quote" | |
costrequired | integer | Price in tokens; repeat the request with confirmCost equal to it. For an operation at cost (atCost) — the maximum: the actual cost is charged, never more |
freerequired | boolean | The operation is free (first video, free retry) |
estimate | number | Operation at cost: expected price in tokens (fractional) |
atCost | boolean | Priced at the AI providers’ cost, no markup: the wallet is charged the actual cost, at most cost |
availablerequired | integer | Tokens available now |
enoughrequired | boolean | Enough tokens to run |
spendLimitLeftrequired | integer | null | What is left of the token's monthly spend limit; null — no limit |
Response fields: TranscribeJob (5)
| Field | Type | Description |
|---|---|---|
idrequired | string | |
statusrequired | string | processing · ready · failed |
sourcesrequired | integer[] | |
createdAtrequired | string | |
error | string |
{
"ok": true,
"data": {
"status": "quote",
"cost": 24,
"free": true,
"estimate": 1.5,
"atCost": true,
"available": 240,
"enough": true,
"spendLimitLeft": 200
}
}Speech recognition jobs
/v1/projects/{id}/transcribePath parameters
| Field | Type | Description |
|---|---|---|
idrequired | integer | ≥ 1 |
Request
curl "https://api.slicer.media/v1/projects/42/transcribe" \
-H "Authorization: Bearer $SLICER_TOKEN"const response = await fetch('https://api.slicer.media/v1/projects/42/transcribe', {
headers: {
Authorization: `Bearer ${process.env.SLICER_TOKEN}`,
},
});
const { ok, data, error } = await response.json();import os
import requests
response = requests.get(
"https://api.slicer.media/v1/projects/42/transcribe",
headers={"Authorization": f"Bearer {os.environ['SLICER_TOKEN']}"},
)
body = response.json()Response
{
"ok": true,
"data": [
{
"id": "string",
"status": "processing",
"sources": [
1
],
"createdAt": "2026-10-16T09:30:00Z",
"error": "string"
}
]
}Command for the AI assistant
/v1/projects/{id}/assistantCharged only if something changed. The result is saved.
Path parameters
| Field | Type | Description |
|---|---|---|
idrequired | integer | ≥ 1 |
Request body AssistantRequest
| Field | Type | Description |
|---|---|---|
commandrequired | string | What to change, in any language≤ 500 |
revrequired | integer | |
confirmCost | integer | Price you agree to pay. Without it the response is a Quote and nothing starts |
Request
curl -X POST "https://api.slicer.media/v1/projects/42/assistant" \
-H "Authorization: Bearer $SLICER_TOKEN" \
-H "Content-Type: application/json" \
-d '{"command":"Сделай субтитры крупнее","rev":7}'const response = await fetch('https://api.slicer.media/v1/projects/42/assistant', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.SLICER_TOKEN}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
"command": "Сделай субтитры крупнее",
"rev": 7
}),
});
const { ok, data, error } = await response.json();import os
import requests
response = requests.post(
"https://api.slicer.media/v1/projects/42/assistant",
headers={"Authorization": f"Bearer {os.environ['SLICER_TOKEN']}"},
json={
"command": "Сделай субтитры крупнее",
"rev": 7,
},
)
body = response.json()Response
One of: Quote, AssistantResult
Response fields: Quote (8)
| Field | Type | Description |
|---|---|---|
statusrequired | "quote" | |
costrequired | integer | Price in tokens; repeat the request with confirmCost equal to it. For an operation at cost (atCost) — the maximum: the actual cost is charged, never more |
freerequired | boolean | The operation is free (first video, free retry) |
estimate | number | Operation at cost: expected price in tokens (fractional) |
atCost | boolean | Priced at the AI providers’ cost, no markup: the wallet is charged the actual cost, at most cost |
availablerequired | integer | Tokens available now |
enoughrequired | boolean | Enough tokens to run |
spendLimitLeftrequired | integer | null | What is left of the token's monthly spend limit; null — no limit |
Response fields: AssistantResult (6)
| Field | Type | Description |
|---|---|---|
revrequired | integer | |
summaryrequired | string | |
appliedrequired | integer | |
chargedrequired | integer | Tokens charged from the wallet (whole) |
costTokens | number | Actual cost of the command in tokens (fractional); the remainder accumulates with the project |
durationSecrequired | number |
{
"ok": true,
"data": {
"status": "quote",
"cost": 24,
"free": true,
"estimate": 1.5,
"atCost": true,
"available": 240,
"enough": true,
"spendLimitLeft": 200
}
}Translate captions
/v1/projects/{id}/translatePath parameters
| Field | Type | Description |
|---|---|---|
idrequired | integer | ≥ 1 |
Request body TranslateRequest
| Field | Type | Description |
|---|---|---|
languagerequired | string | en · es · pt · de · fr · it · pl · tr · id · uk · ru |
revrequired | integer | |
confirmCost | integer | Price you agree to pay. Without it the response is a Quote and nothing starts |
Request
curl -X POST "https://api.slicer.media/v1/projects/42/translate" \
-H "Authorization: Bearer $SLICER_TOKEN" \
-H "Content-Type: application/json" \
-d '{"language":"en","rev":7}'const response = await fetch('https://api.slicer.media/v1/projects/42/translate', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.SLICER_TOKEN}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
"language": "en",
"rev": 7
}),
});
const { ok, data, error } = await response.json();import os
import requests
response = requests.post(
"https://api.slicer.media/v1/projects/42/translate",
headers={"Authorization": f"Bearer {os.environ['SLICER_TOKEN']}"},
json={
"language": "en",
"rev": 7,
},
)
body = response.json()Response
One of: Quote, TranslateResult
Response fields: Quote (8)
| Field | Type | Description |
|---|---|---|
statusrequired | "quote" | |
costrequired | integer | Price in tokens; repeat the request with confirmCost equal to it. For an operation at cost (atCost) — the maximum: the actual cost is charged, never more |
freerequired | boolean | The operation is free (first video, free retry) |
estimate | number | Operation at cost: expected price in tokens (fractional) |
atCost | boolean | Priced at the AI providers’ cost, no markup: the wallet is charged the actual cost, at most cost |
availablerequired | integer | Tokens available now |
enoughrequired | boolean | Enough tokens to run |
spendLimitLeftrequired | integer | null | What is left of the token's monthly spend limit; null — no limit |
Response fields: TranslateResult (4)
| Field | Type | Description |
|---|---|---|
revrequired | integer | |
phrasesrequired | integer | |
chargedrequired | integer | Tokens charged from the wallet (whole) |
costTokens | number | Actual cost of the translation in tokens (fractional) |
{
"ok": true,
"data": {
"status": "quote",
"cost": 24,
"free": true,
"estimate": 1.5,
"atCost": true,
"available": 240,
"enough": true,
"spendLimitLeft": 200
}
}Auto colour and colour matching (free)
/v1/projects/{id}/auto-colorNo AI: statistics of 10 frames of the fragment → «Adjust» values (exposure, contrast, saturation, white balance). The result is applied and saved.
Path parameters
| Field | Type | Description |
|---|---|---|
idrequired | integer | ≥ 1 |
Request body AutoColorRequest
| Field | Type | Description |
|---|---|---|
revrequired | integer | Revision from GET …/transcript or …/document |
blockIdrequired | string | Fragment id: the one to correct (auto) or the sample (match)≤ 64 |
moderequired | string | auto — balance this fragment; match — make all other fragments look like it (with its filter and LUT)auto · match |
Request
curl -X POST "https://api.slicer.media/v1/projects/42/auto-color" \
-H "Authorization: Bearer $SLICER_TOKEN" \
-H "Content-Type: application/json" \
-d '{"rev":7,"blockId":"string","mode":"auto"}'const response = await fetch('https://api.slicer.media/v1/projects/42/auto-color', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.SLICER_TOKEN}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
"rev": 7,
"blockId": "string",
"mode": "auto"
}),
});
const { ok, data, error } = await response.json();import os
import requests
response = requests.post(
"https://api.slicer.media/v1/projects/42/auto-color",
headers={"Authorization": f"Bearer {os.environ['SLICER_TOKEN']}"},
json={
"rev": 7,
"blockId": "string",
"mode": "auto",
},
)
body = response.json()Response
Response fields: AutoColorResult (5)
| Field | Type | Description |
|---|---|---|
revrequired | integer | |
moderequired | string | auto · match |
adjustrequired | Record<string, object> | New «Adjust» values by fragment id |
appliedrequired | integer | |
durationSecrequired | number |
{
"ok": true,
"data": {
"rev": 7,
"mode": "auto",
"adjust": {},
"applied": 1,
"durationSec": 42.5
}
}Change the style
/v1/projects/{id}/stylePath parameters
| Field | Type | Description |
|---|---|---|
idrequired | integer | ≥ 1 |
Request body StyleRequest
| Field | Type | Description |
|---|---|---|
templateId | string | |
fontId | string | |
colorId | string | |
subtitles | object | Caption look; a partial change keeps the other fields. A new design without highlight/animation/words resets them to auto |
subtitles.enabled | boolean | |
subtitles.design | string | dynamic · karaoke · classic · minimal · pop · box · word |
subtitles.size | string | small · medium · large |
subtitles.position | string | auto — each phrase where the frame has the most free space (off the face, hook, plaques, titles)auto · top · center · bottom |
subtitles.speakerColors | boolean | Each speaker in its own colour |
subtitles.highlight | string | The spoken word: colour, pop (grows), box, underline; auto — as the design intendsauto · none · color · pop · box · underline |
subtitles.animation | string | Phrase entrance; words — word by word as spokenauto · none · fade · pop · slide · words |
subtitles.words | string | Words on screen at onceauto · one · few · line · phrase |
subtitles.letterCase | string | Letter case; auto — as the font intendsauto · upper · normal |
params | object | |
params.soundEffects | boolean | |
params.introZoom | boolean |
Request
curl -X PATCH "https://api.slicer.media/v1/projects/42/style" \
-H "Authorization: Bearer $SLICER_TOKEN" \
-H "Content-Type: application/json" \
-d '{"templateId":"string","fontId":"string"}'const response = await fetch('https://api.slicer.media/v1/projects/42/style', {
method: 'PATCH',
headers: {
Authorization: `Bearer ${process.env.SLICER_TOKEN}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
"templateId": "string",
"fontId": "string"
}),
});
const { ok, data, error } = await response.json();import os
import requests
response = requests.patch(
"https://api.slicer.media/v1/projects/42/style",
headers={"Authorization": f"Bearer {os.environ['SLICER_TOKEN']}"},
json={
"templateId": "string",
"fontId": "string",
},
)
body = response.json()Response
Response fields: Project (26)
| Field | Type | Description |
|---|---|---|
idrequired | integer | |
titlerequired | string | |
statusrequired | string | draft — an “edit it myself” project before its first export: edit it with operations, then exportqueued · processing · succeeded · failed · cancelled · draft |
moderequired | string | ai — edited by the AI director; manual — materials on the timeline as is, free (only AI operations cost)ai · manual |
stagerequired | string | null | |
progressrequired | integer | Progress of the current run, % |
clientrequired | string | null | Where it was created: web, mobile, api, mcp |
settingsrequired | object | Montage settings (see MontageSettingsInput) |
briefrequired | string | null | |
isFreerequired | boolean | |
watermarkrequired | boolean | |
tokensCostrequired | integer | |
errorrequired | object | null | |
error.coderequired | string | |
error.messagerequired | string | |
durationSecrequired | number | null | |
versionChoicerequired | integer[] | null | Version ids awaiting your choice; a montage returns one version, so usually null |
resultrequired | object | null | |
result.urlrequired | string (uri) | Download with the token (Range supported) |
result.posterUrlrequired | string (uri) | null | |
result.containerrequired | string | |
result.inforequired | object | null | |
appUrlrequired | string (uri) | The project in the Slicer web app |
sourcesAvailablerequired | boolean | Sources are kept 7 days after the montage |
createdAtrequired | string | ISO 8601 date-time, UTC |
finishedAtrequired | string | null |
{
"ok": true,
"data": {
"id": 42,
"title": "Монтаж за 3 минуты",
"status": "queued",
"mode": "ai",
"stage": "string",
"progress": 0.5,
"client": "string",
"settings": {},
"brief": "Динамично, с акцентом на главные мысли",
"isFree": true,
"watermark": true,
"tokensCost": 24,
"error": {
"code": "string",
"message": "string"
},
"durationSec": 42.5,
"versionChoice": [
1
],
"result": {
"url": "https://example.com/hook",
"posterUrl": "https://api.slicer.media/v1/…",
"container": "string",
"info": {}
},
"appUrl": "https://api.slicer.media/v1/…",
"sourcesAvailable": true,
"createdAt": "2026-10-16T09:30:00Z",
"finishedAt": "2026-10-16T09:30:00Z"
}
}Your own director
Your AI agent edits the video itself — for free: a brief with the material and the plan and observation schemas, frames of the sources and accepting the plan with observations from the frames. The rules of the Slicer AI director are applied to the plan on the server; the answer has fixes and issues. How to use them — Your own director.
Director brief for your AI assistant (free)
/v1/projects/{id}/director/briefThe material (speech by word ids, pauses, retakes, wishes), general tips and the schemas of the plan and of scenes. Your assistant looks at the footage with sourceFrames and sends the plan with what it saw to submitDirectorPlan; the rules of the Slicer director run on the server there. Optional settings are saved to the project.
Path parameters
| Field | Type | Description |
|---|---|---|
idrequired | integer | ≥ 1 |
Request body DirectorBriefRequest
| Field | Type | Description |
|---|---|---|
mode | string | Video typeauto · talking · story · product · aesthetic |
pace | string | calm · balanced · dynamic |
effects | string | off · light · bold |
targetDurationSec | integer | null | Target length, s; null — the director decides (up to 90 s)10–180 |
hook | boolean | Open with a hook and an on-screen title |
plaques | boolean | |
emoji | boolean | |
cameraMotion | boolean | Zooms and camera moves |
Request
curl -X POST "https://api.slicer.media/v1/projects/42/director/brief" \
-H "Authorization: Bearer $SLICER_TOKEN" \
-H "Content-Type: application/json" \
-d '{"mode":"auto","pace":"calm"}'const response = await fetch('https://api.slicer.media/v1/projects/42/director/brief', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.SLICER_TOKEN}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
"mode": "auto",
"pace": "calm"
}),
});
const { ok, data, error } = await response.json();import os
import requests
response = requests.post(
"https://api.slicer.media/v1/projects/42/director/brief",
headers={"Authorization": f"Bearer {os.environ['SLICER_TOKEN']}"},
json={
"mode": "auto",
"pace": "calm",
},
)
body = response.json()Response
Response fields: DirectorBrief (15)
| Field | Type | Description |
|---|---|---|
projectIdrequired | integer | |
revrequired | integer | |
settingsrequired | Record<string, object> | |
sourcesrequired | object[] | |
sources[].indexrequired | integer | |
sources[].kindrequired | string | |
sources[].durationSecrequired | number | |
sources[].fileNamerequired | string | null | |
sources[].wordsrequired | integer | |
speechRecognizedrequired | boolean | |
materialrequired | string | The material: sources, speech by word ids, pauses, retakes, wishes |
guiderequired | string[] | General tips (the Slicer director rules run on the server when you submit the plan) |
planSchemarequired | Record<string, object> | JSON schema of the plan for submitDirectorPlan |
scenesSchemarequired | Record<string, object> | JSON schema of what you saw in the frames |
nextrequired | string[] |
{
"ok": true,
"data": {
"projectId": 42,
"rev": 7,
"settings": {},
"sources": [
{
"index": 1,
"kind": "string",
"durationSec": 42.5,
"fileName": "interview.mp4",
"words": 1
}
],
"speechRecognized": true,
"material": "string",
"guide": [
"string"
],
"planSchema": {},
"scenesSchema": {},
"next": [
"string"
]
}
}Frames of a source (free)
/v1/projects/{id}/sources/{index}/framesPath parameters
| Field | Type | Description |
|---|---|---|
idrequired | integer | ≥ 1 |
indexrequired | integer |
Query parameters
| Field | Type | Description |
|---|---|---|
times | string | |
from | number | |
to | number | |
count | integer | 1–16 |
Request
curl "https://api.slicer.media/v1/projects/42/sources/1/frames" \
-H "Authorization: Bearer $SLICER_TOKEN"const response = await fetch('https://api.slicer.media/v1/projects/42/sources/1/frames', {
headers: {
Authorization: `Bearer ${process.env.SLICER_TOKEN}`,
},
});
const { ok, data, error } = await response.json();import os
import requests
response = requests.get(
"https://api.slicer.media/v1/projects/42/sources/1/frames",
headers={"Authorization": f"Bearer {os.environ['SLICER_TOKEN']}"},
)
body = response.json()Response
Response fields: SourceFrames (6)
| Field | Type | Description |
|---|---|---|
sourceIndexrequired | integer | |
durationSecrequired | number | |
framesrequired | object[] | |
frames[].timerequired | number | |
frames[].mimeTyperequired | "image/jpeg" | |
frames[].datarequired | string | JPEG, base64, 512 px on the longer side |
{
"ok": true,
"data": {
"sourceIndex": 1,
"durationSec": 42.5,
"frames": [
{
"time": 1.5,
"mimeType": "image/jpeg",
"data": "string"
}
]
}
}Apply a director plan (free)
/v1/projects/{id}/director/planThe rules of the Slicer director are applied to the plan on the server (word boundaries, retakes, footage by your scenes, restraint, length): the plan becomes the video document — a new revision, visible in the editor — and the answer lists fixes and issues to address. Media cards from a description are not accepted (their picture is a paid generation).
Path parameters
| Field | Type | Description |
|---|---|---|
idrequired | integer | ≥ 1 |
Request body DirectorPlanRequest
| Field | Type | Description |
|---|---|---|
revrequired | integer | rev from directorBrief / transcript |
planrequired | Record<string, object> | The plan in DirectorBrief.planSchema |
scenes | Record<string, object>[] | What you saw in the frames, in DirectorBrief.scenesSchema0–400 |
Request
curl -X POST "https://api.slicer.media/v1/projects/42/director/plan" \
-H "Authorization: Bearer $SLICER_TOKEN" \
-H "Content-Type: application/json" \
-d '{"rev":7,"plan":{}}'const response = await fetch('https://api.slicer.media/v1/projects/42/director/plan', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.SLICER_TOKEN}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
"rev": 7,
"plan": {}
}),
});
const { ok, data, error } = await response.json();import os
import requests
response = requests.post(
"https://api.slicer.media/v1/projects/42/director/plan",
headers={"Authorization": f"Bearer {os.environ['SLICER_TOKEN']}"},
json={
"rev": 7,
"plan": {},
},
)
body = response.json()Response
Response fields: DirectorPlanResult (10)
| Field | Type | Description |
|---|---|---|
revrequired | integer | |
durationSecrequired | number | |
planSegmentsrequired | integer | |
fragmentsrequired | integer | |
fixesrequired | string[] | What Slicer corrected by its director rules |
issuesrequired | object[] | What you should fix and submit again |
issues[].coderequired | string | |
issues[].segment | integer | |
issues[].messagerequired | string | |
videorequired | string | The resulting video as text (fragments, words, elements) — check it against the plan |
{
"ok": true,
"data": {
"rev": 7,
"durationSec": 42.5,
"planSegments": 1,
"fragments": 1,
"fixes": [
"string"
],
"issues": [
{
"code": "string",
"segment": 1,
"message": "string"
}
],
"video": "string"
}
}Edit document
Edit document
/v1/projects/{id}/documentPath parameters
| Field | Type | Description |
|---|---|---|
idrequired | integer | ≥ 1 |
Request
curl "https://api.slicer.media/v1/projects/42/document" \
-H "Authorization: Bearer $SLICER_TOKEN"const response = await fetch('https://api.slicer.media/v1/projects/42/document', {
headers: {
Authorization: `Bearer ${process.env.SLICER_TOKEN}`,
},
});
const { ok, data, error } = await response.json();import os
import requests
response = requests.get(
"https://api.slicer.media/v1/projects/42/document",
headers={"Authorization": f"Bearer {os.environ['SLICER_TOKEN']}"},
)
body = response.json()Response
Response fields: DocumentState (3)
| Field | Type | Description |
|---|---|---|
revrequired | integer | |
durationSecrequired | number | |
documentrequired | object | Edit document (EditDocument v1). Fields are only added within v1 |
{
"ok": true,
"data": {
"rev": 7,
"durationSec": 42.5,
"document": {}
}
}Replace the edit document (advanced)
/v1/projects/{id}/documentPath parameters
| Field | Type | Description |
|---|---|---|
idrequired | integer | ≥ 1 |
Request body PutDocumentRequest
| Field | Type | Description |
|---|---|---|
revrequired | integer | |
documentrequired | object |
Request
curl -X PUT "https://api.slicer.media/v1/projects/42/document" \
-H "Authorization: Bearer $SLICER_TOKEN" \
-H "Content-Type: application/json" \
-d '{"rev":7,"document":{}}'const response = await fetch('https://api.slicer.media/v1/projects/42/document', {
method: 'PUT',
headers: {
Authorization: `Bearer ${process.env.SLICER_TOKEN}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
"rev": 7,
"document": {}
}),
});
const { ok, data, error } = await response.json();import os
import requests
response = requests.put(
"https://api.slicer.media/v1/projects/42/document",
headers={"Authorization": f"Bearer {os.environ['SLICER_TOKEN']}"},
json={
"rev": 7,
"document": {},
},
)
body = response.json()Response
Response fields: DocumentState (3)
| Field | Type | Description |
|---|---|---|
revrequired | integer | |
durationSecrequired | number | |
documentrequired | object | Edit document (EditDocument v1). Fields are only added within v1 |
{
"ok": true,
"data": {
"rev": 7,
"durationSec": 42.5,
"document": {}
}
}Schemas
Transcript
| Field | Type | Description |
|---|---|---|
revrequired | integer | Document revision: pass it to edits |
durationSecrequired | number | |
textrequired | string | The video as text for an LLM: fragments with word ids w<id>, cut words in [brackets], graphics |
blocksrequired | object[] | |
blocks[].idrequired | string | |
blocks[].positionrequired | integer | |
blocks[].sourceIndexrequired | integer | |
blocks[].kindrequired | string | words · range |
blocks[].wordsrequired | object[] | |
blocks[].words[].idrequired | integer | |
blocks[].words[].textrequired | string | |
blocks[].words[].cutrequired | boolean | |
blocks[].words[].accentrequired | boolean | |
elementsrequired | object | |
elements.hookrequired | string | null | |
elements.plaquesrequired | object[] | |
elements.plaques[].idrequired | string | |
elements.plaques[].kindrequired | string | |
elements.plaques[].titlerequired | string | |
elements.plaques[].fromWordIdrequired | integer | null | Word id; null — the element is placed by time (anchor) |
elements.plaques[].toWordIdrequired | integer | null | Word id; null — the element is placed by time (anchor) |
elements.plaques[].start | number | Where the element starts in the video, s; absent — hidden (its words were cut) |
elements.plaques[].end | number | Where the element ends in the video, s |
elements.plaques[].anchor | object | Placed by time: fragment, second of its source and duration in the video, s |
elements.plaques[].anchor.blockIdrequired | string | |
elements.plaques[].anchor.atrequired | number | |
elements.plaques[].anchor.durationrequired | number | |
elements.cardsrequired | object[] | |
elements.cards[].idrequired | string | |
elements.cards[].captionrequired | string | null | |
elements.cards[].fromWordIdrequired | integer | null | Word id; null — the element is placed by time (anchor) |
elements.cards[].toWordIdrequired | integer | null | Word id; null — the element is placed by time (anchor) |
elements.cards[].start | number | Where the element starts in the video, s; absent — hidden (its words were cut) |
elements.cards[].end | number | Where the element ends in the video, s |
elements.cards[].anchor | object | Placed by time: fragment, second of its source and duration in the video, s |
elements.cards[].anchor.blockIdrequired | string | |
elements.cards[].anchor.atrequired | number | |
elements.cards[].anchor.durationrequired | number | |
elements.emojirequired | object[] | |
elements.emoji[].idrequired | string | |
elements.emoji[].emojirequired | string | |
elements.emoji[].wordIdrequired | integer | null | Word id; null — the element is placed by time (anchor) |
elements.emoji[].start | number | Where the element starts in the video, s; absent — hidden (its words were cut) |
elements.emoji[].end | number | Where the element ends in the video, s |
elements.emoji[].anchor | object | Placed by time: fragment, second of its source and duration in the video, s |
elements.emoji[].anchor.blockIdrequired | string | |
elements.emoji[].anchor.atrequired | number | |
elements.emoji[].anchor.durationrequired | number | |
elements.zoomsrequired | object[] | |
elements.zooms[].idrequired | string | |
elements.zooms[].fromWordIdrequired | integer | null | Word id; null — the element is placed by time (anchor) |
elements.zooms[].toWordIdrequired | integer | null | Word id; null — the element is placed by time (anchor) |
elements.zooms[].start | number | Where the element starts in the video, s; absent — hidden (its words were cut) |
elements.zooms[].end | number | Where the element ends in the video, s |
elements.zooms[].anchor | object | Placed by time: fragment, second of its source and duration in the video, s |
elements.zooms[].anchor.blockIdrequired | string | |
elements.zooms[].anchor.atrequired | number | |
elements.zooms[].anchor.durationrequired | number | |
elements.titlesrequired | object[] | Free text layers (titles) |
elements.titles[].idrequired | string | |
elements.titles[].textrequired | string | |
elements.titles[].fontrequired | string | |
elements.titles[].sizerequired | number | |
elements.titles[].colorrequired | string | |
elements.titles[].backgroundrequired | string | none · box · pill |
elements.titles[].backgroundColor | string | |
elements.titles[].outline | boolean | |
elements.titles[].shadow | boolean | |
elements.titles[].alignrequired | string | left · center · right |
elements.titles[].xrequired | number | |
elements.titles[].yrequired | number | |
elements.titles[].animIn | string | fade · slide · pop · typewriter |
elements.titles[].animOut | string | fade · slide · pop |
elements.titles[].fromWordIdrequired | integer | null | Word id; null — the element is placed by time (anchor) |
elements.titles[].toWordIdrequired | integer | null | Word id; null — the element is placed by time (anchor) |
elements.titles[].start | number | Where the element starts in the video, s; absent — hidden (its words were cut) |
elements.titles[].end | number | Where the element ends in the video, s |
elements.titles[].anchor | object | Placed by time: fragment, second of its source and duration in the video, s |
elements.titles[].anchor.blockIdrequired | string | |
elements.titles[].anchor.atrequired | number | |
elements.titles[].anchor.durationrequired | number | |
elements.markersrequired | object[] | Author markers on the timeline (not shown in the video); hidden markers are omitted |
elements.markers[].idrequired | string | |
elements.markers[].timerequired | number | |
elements.markers[].labelrequired | string | null | |
elements.markers[].colorrequired | string | cyan · yellow · red · green · violet |
elements.soundsrequired | object[] | Sound effects placed by the author; cut-off ones are omitted |
elements.sounds[].idrequired | string | |
elements.sounds[].kindrequired | string | whoosh · pop · ding · click · swipe · rise · boom · chime |
elements.sounds[].timerequired | number |
EditOperation
| Field | Type | Description |
|---|
EditsResult
| Field | Type | Description |
|---|---|---|
revrequired | integer | |
appliedrequired | integer | |
skippedrequired | object[] | |
skipped[].indexrequired | integer | |
skipped[].oprequired | string | |
skipped[].reasonrequired | string | |
durationSecrequired | number |
TranscribeRequest
| Field | Type | Description |
|---|---|---|
sources | integer[] | Source indexes (default — every source with sound whose speech is not recognised yet)1–40 |
language | string | Speech language: auto or a two-letter code |
confirmCost | integer | Price you agree to pay. Without it the response is a Quote and nothing starts |
TranscribeJob
| Field | Type | Description |
|---|---|---|
idrequired | string | |
statusrequired | string | processing · ready · failed |
sourcesrequired | integer[] | |
createdAtrequired | string | |
error | string |
AssistantResult
| Field | Type | Description |
|---|---|---|
revrequired | integer | |
summaryrequired | string | |
appliedrequired | integer | |
chargedrequired | integer | Tokens charged from the wallet (whole) |
costTokens | number | Actual cost of the command in tokens (fractional); the remainder accumulates with the project |
durationSecrequired | number |
TranslateResult
| Field | Type | Description |
|---|---|---|
revrequired | integer | |
phrasesrequired | integer | |
chargedrequired | integer | Tokens charged from the wallet (whole) |
costTokens | number | Actual cost of the translation in tokens (fractional) |
StyleRequest
| Field | Type | Description |
|---|---|---|
templateId | string | |
fontId | string | |
colorId | string | |
subtitles | object | Caption look; a partial change keeps the other fields. A new design without highlight/animation/words resets them to auto |
subtitles.enabled | boolean | |
subtitles.design | string | dynamic · karaoke · classic · minimal · pop · box · word |
subtitles.size | string | small · medium · large |
subtitles.position | string | auto — each phrase where the frame has the most free space (off the face, hook, plaques, titles)auto · top · center · bottom |
subtitles.speakerColors | boolean | Each speaker in its own colour |
subtitles.highlight | string | The spoken word: colour, pop (grows), box, underline; auto — as the design intendsauto · none · color · pop · box · underline |
subtitles.animation | string | Phrase entrance; words — word by word as spokenauto · none · fade · pop · slide · words |
subtitles.words | string | Words on screen at onceauto · one · few · line · phrase |
subtitles.letterCase | string | Letter case; auto — as the font intendsauto · upper · normal |
params | object | |
params.soundEffects | boolean | |
params.introZoom | boolean |
DirectorBriefRequest
| Field | Type | Description |
|---|---|---|
mode | string | Video typeauto · talking · story · product · aesthetic |
pace | string | calm · balanced · dynamic |
effects | string | off · light · bold |
targetDurationSec | integer | null | Target length, s; null — the director decides (up to 90 s)10–180 |
hook | boolean | Open with a hook and an on-screen title |
plaques | boolean | |
emoji | boolean | |
cameraMotion | boolean | Zooms and camera moves |
DirectorBrief
| Field | Type | Description |
|---|---|---|
projectIdrequired | integer | |
revrequired | integer | |
settingsrequired | Record<string, object> | |
sourcesrequired | object[] | |
sources[].indexrequired | integer | |
sources[].kindrequired | string | |
sources[].durationSecrequired | number | |
sources[].fileNamerequired | string | null | |
sources[].wordsrequired | integer | |
speechRecognizedrequired | boolean | |
materialrequired | string | The material: sources, speech by word ids, pauses, retakes, wishes |
guiderequired | string[] | General tips (the Slicer director rules run on the server when you submit the plan) |
planSchemarequired | Record<string, object> | JSON schema of the plan for submitDirectorPlan |
scenesSchemarequired | Record<string, object> | JSON schema of what you saw in the frames |
nextrequired | string[] |
SourceFrames
| Field | Type | Description |
|---|---|---|
sourceIndexrequired | integer | |
durationSecrequired | number | |
framesrequired | object[] | |
frames[].timerequired | number | |
frames[].mimeTyperequired | "image/jpeg" | |
frames[].datarequired | string | JPEG, base64, 512 px on the longer side |
DirectorPlanRequest
| Field | Type | Description |
|---|---|---|
revrequired | integer | rev from directorBrief / transcript |
planrequired | Record<string, object> | The plan in DirectorBrief.planSchema |
scenes | Record<string, object>[] | What you saw in the frames, in DirectorBrief.scenesSchema0–400 |
DirectorPlanResult
| Field | Type | Description |
|---|---|---|
revrequired | integer | |
durationSecrequired | number | |
planSegmentsrequired | integer | |
fragmentsrequired | integer | |
fixesrequired | string[] | What Slicer corrected by its director rules |
issuesrequired | object[] | What you should fix and submit again |
issues[].coderequired | string | |
issues[].segment | integer | |
issues[].messagerequired | string | |
videorequired | string | The resulting video as text (fragments, words, elements) — check it against the plan |
DocumentState
| Field | Type | Description |
|---|---|---|
revrequired | integer | |
durationSecrequired | number | |
documentrequired | object | Edit document (EditDocument v1). Fields are only added within v1 |