# Projects

> Slicer API project endpoints — starting a montage with price confirmation, status and stages, video versions, rebuild, retry, stop, history and downloads.

Source: https://docs.slicer.media/en/api/reference/projects/

A project is a video and everything that happened to it: materials, montage and export runs, versions, history. Paid operations (create with an AI montage, rebuild, retry) answer with a price first and start only with `confirmCost` — see [Montage and pricing](https://docs.slicer.media/en/api/montage/). A project without the AI director (`mode: "manual"`) is created for free and without confirmation — see [Project without the AI director](https://docs.slicer.media/en/api/montage/#manual).

## Create a project and start the montage

`POST /v1/projects`

- **Scopes:** `projects:write`
- **Rate limit:** paid operations
- **Price:** _spends tokens_

Without `confirmCost` returns a `Quote` and creates nothing. With `confirmCost` equal to the current price creates the project and starts the montage: one version that the director checks and fixes after rendering. The first video is free (`cost: 0`). With `mode: "manual"` the project is free: materials go to the timeline in order and the project becomes a `draft` — no `confirmCost` needed.

> **Important.** Without `confirmCost` it returns the price (`Quote`) and starts nothing.

### Request body `CreateProjectRequest`

| Field | Type | Description |
| --- | --- | --- |
| `uploadIds` _(required)_ | `string (uuid)[]` | Ready `source` uploads in material order — 1–20 |
| `mode` | `string` | `ai` (default) — the AI director edits the video, paid; `manual` — materials go to the timeline in order, the project is a free `draft` you edit and export yourself — `ai` · `manual` |
| `format` | `string` | Frame format of a `manual` project (default 9:16) — `9:16` · `4:5` · `1:1` · `16:9` |
| `settings` | `MontageSettingsInput` |     |
| `title` | `string` | — ≤ 120 |
| `brief` | `string` | Wishes for the AI director — ≤ 600 |
| `confirmCost` | `integer` | Price you agree to pay. Without it the response is a `Quote` and nothing starts |

### Request

**curl**

```bash
curl -X POST "https://api.slicer.media/v1/projects" \
  -H "Authorization: Bearer $SLICER_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"uploadIds":["6f1c2a4e-8b3d-4c1a-9e2f-3b5d7a9c1e20"]}'
```

**JavaScript**

```js
const response = await fetch('https://api.slicer.media/v1/projects', {
  method: 'POST',
  headers: {
    Authorization: `Bearer ${process.env.SLICER_TOKEN}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    "uploadIds": [
      "6f1c2a4e-8b3d-4c1a-9e2f-3b5d7a9c1e20"
    ]
  }),
});
const { ok, data, error } = await response.json();
```

**Python**

```python
import os
import requests

response = requests.post(
    "https://api.slicer.media/v1/projects",
    headers={"Authorization": f"Bearer {os.environ['SLICER_TOKEN']}"},
    json={
        "uploadIds": ["6f1c2a4e-8b3d-4c1a-9e2f-3b5d7a9c1e20"],
    },
)
body = response.json()
```

### Response

One of: Quote, ProjectDetails

**Response fields: `Quote` (8)**

| Field | Type | Description |
| --- | --- | --- |
| `status` _(required)_ | `"quote"` |     |
| `cost` _(required)_ | `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 |
| `free` _(required)_ | `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` |
| `available` _(required)_ | `integer` | Tokens available now |
| `enough` _(required)_ | `boolean` | Enough tokens to run |
| `spendLimitLeft` _(required)_ | `integer \| null` | What is left of the token's monthly spend limit; `null` — no limit |

**Response fields: `ProjectDetails` (44)**

| Field | Type | Description |
| --- | --- | --- |
| `id` _(required)_ | `integer` |     |
| `title` _(required)_ | `string` |     |
| `status` _(required)_ | `string` | `draft` — an “edit it myself” project before its first export: edit it with operations, then export — `queued` · `processing` · `succeeded` · `failed` · `cancelled` · `draft` |
| `mode` _(required)_ | `string` | `ai` — edited by the AI director; `manual` — materials on the timeline as is, free (only AI operations cost) — `ai` · `manual` |
| `stage` _(required)_ | `string \| null` |     |
| `progress` _(required)_ | `integer` | Progress of the current run, % |
| `client` _(required)_ | `string \| null` | Where it was created: web, mobile, api, mcp |
| `settings` _(required)_ | `object` | Montage settings (see MontageSettingsInput) |
| `brief` _(required)_ | `string \| null` |     |
| `isFree` _(required)_ | `boolean` |     |
| `watermark` _(required)_ | `boolean` |     |
| `tokensCost` _(required)_ | `integer` |     |
| `error` _(required)_ | `object \| null` |     |
| `error.code` _(required)_ | `string` |     |
| `error.message` _(required)_ | `string` |     |
| `durationSec` _(required)_ | `number \| null` |     |
| `versionChoice` _(required)_ | `integer[] \| null` | Version ids awaiting your choice; a montage returns one version, so usually `null` |
| `result` _(required)_ | `object \| null` |     |
| `result.url` _(required)_ | `string (uri)` | Download with the token (Range supported) |
| `result.posterUrl` _(required)_ | `string (uri) \| null` |     |
| `result.container` _(required)_ | `string` |     |
| `result.info` _(required)_ | `object \| null` |     |
| `appUrl` _(required)_ | `string (uri)` | The project in the Slicer web app |
| `sourcesAvailable` _(required)_ | `boolean` | Sources are kept 7 days after the montage |
| `createdAt` _(required)_ | `string` | ISO 8601 date-time, UTC |
| `finishedAt` _(required)_ | `string \| null` |     |
| `stages` _(required)_ | `object[]` |     |
| `stages[].stage` _(required)_ | `string` |     |
| `stages[].status` _(required)_ | `string` | — `pending` · `running` · `succeeded` · `failed` · `skipped` |
| `stages[].inRun` _(required)_ | `boolean` | Part of the current run (not reused from a previous one) |
| `stages[].startedAt` _(required)_ | `string \| null` |     |
| `stages[].finishedAt` _(required)_ | `string \| null` |     |
| `stages[].durationSec` _(required)_ | `number \| null` |     |
| `stages[].progress` _(required)_ | `integer` |     |
| `stages[].tokens` _(required)_ | `integer` | Share of the run price |
| `run` _(required)_ | `object` |     |
| `run.kind` _(required)_ | `string` |     |
| `run.status` _(required)_ | `string` |     |
| `run.tokens` _(required)_ | `integer` |     |
| `run.tokensState` _(required)_ | `string` | — `free` · `reserved` · `charged` · `refunded` |
| `run.queuedAt` _(required)_ | `string` | ISO 8601 date-time, UTC |
| `run.startedAt` _(required)_ | `string \| null` |     |
| `run.finishedAt` _(required)_ | `string \| null` |     |
| `versions` _(required)_ | `ProjectVersion[]` |     |

Response example:

```json
{
  "ok": true,
  "data": {
    "status": "quote",
    "cost": 24,
    "free": true,
    "estimate": 1.5,
    "atCost": true,
    "available": 240,
    "enough": true,
    "spendLimitLeft": 200
  }
}
```

## List projects

`GET /v1/projects`

- **Scopes:** `projects:read`
- **Rate limit:** default

### Query parameters

| Field | Type | Description |
| --- | --- | --- |
| `limit` | `integer` | — 1–100 |
| `cursor` | `string` | — ≤ 200 |
| `status` | `string` | — `all` · `ready` · `active` · `failed` · `draft` |
| `q` | `string` | — ≤ 100 |

### Request

**curl**

```bash
curl "https://api.slicer.media/v1/projects" \
  -H "Authorization: Bearer $SLICER_TOKEN"
```

**JavaScript**

```js
const response = await fetch('https://api.slicer.media/v1/projects', {
  headers: {
    Authorization: `Bearer ${process.env.SLICER_TOKEN}`,
  },
});
const { ok, data, error } = await response.json();
```

**Python**

```python
import os
import requests

response = requests.get(
    "https://api.slicer.media/v1/projects",
    headers={"Authorization": f"Bearer {os.environ['SLICER_TOKEN']}"},
)
body = response.json()
```

### Response

**Response fields: `object` (2)**

| Field | Type | Description |
| --- | --- | --- |
| `items` _(required)_ | `Project[]` |     |
| `nextCursor` _(required)_ | `string \| null` |     |

Response example:

```json
{
  "ok": true,
  "data": {
    "items": [
      {
        "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": null,
          "message": null
        },
        "durationSec": 42.5,
        "versionChoice": [
          null
        ],
        "result": {
          "url": null,
          "posterUrl": null,
          "container": null,
          "info": null
        },
        "appUrl": "https://api.slicer.media/v1/…",
        "sourcesAvailable": true,
        "createdAt": "2026-10-16T09:30:00Z",
        "finishedAt": "2026-10-16T09:30:00Z"
      }
    ],
    "nextCursor": "eyJpZCI6NDJ9"
  }
}
```

## Project details

`GET /v1/projects/{id}`

- **Scopes:** `projects:read`
- **Rate limit:** default

### Path parameters

| Field | Type | Description |
| --- | --- | --- |
| `id` _(required)_ | `integer` | — ≥ 1 |

### Request

**curl**

```bash
curl "https://api.slicer.media/v1/projects/42" \
  -H "Authorization: Bearer $SLICER_TOKEN"
```

**JavaScript**

```js
const response = await fetch('https://api.slicer.media/v1/projects/42', {
  headers: {
    Authorization: `Bearer ${process.env.SLICER_TOKEN}`,
  },
});
const { ok, data, error } = await response.json();
```

**Python**

```python
import os
import requests

response = requests.get(
    "https://api.slicer.media/v1/projects/42",
    headers={"Authorization": f"Bearer {os.environ['SLICER_TOKEN']}"},
)
body = response.json()
```

### Response

**Response fields: `ProjectDetails` (44)**

| Field | Type | Description |
| --- | --- | --- |
| `id` _(required)_ | `integer` |     |
| `title` _(required)_ | `string` |     |
| `status` _(required)_ | `string` | `draft` — an “edit it myself” project before its first export: edit it with operations, then export — `queued` · `processing` · `succeeded` · `failed` · `cancelled` · `draft` |
| `mode` _(required)_ | `string` | `ai` — edited by the AI director; `manual` — materials on the timeline as is, free (only AI operations cost) — `ai` · `manual` |
| `stage` _(required)_ | `string \| null` |     |
| `progress` _(required)_ | `integer` | Progress of the current run, % |
| `client` _(required)_ | `string \| null` | Where it was created: web, mobile, api, mcp |
| `settings` _(required)_ | `object` | Montage settings (see MontageSettingsInput) |
| `brief` _(required)_ | `string \| null` |     |
| `isFree` _(required)_ | `boolean` |     |
| `watermark` _(required)_ | `boolean` |     |
| `tokensCost` _(required)_ | `integer` |     |
| `error` _(required)_ | `object \| null` |     |
| `error.code` _(required)_ | `string` |     |
| `error.message` _(required)_ | `string` |     |
| `durationSec` _(required)_ | `number \| null` |     |
| `versionChoice` _(required)_ | `integer[] \| null` | Version ids awaiting your choice; a montage returns one version, so usually `null` |
| `result` _(required)_ | `object \| null` |     |
| `result.url` _(required)_ | `string (uri)` | Download with the token (Range supported) |
| `result.posterUrl` _(required)_ | `string (uri) \| null` |     |
| `result.container` _(required)_ | `string` |     |
| `result.info` _(required)_ | `object \| null` |     |
| `appUrl` _(required)_ | `string (uri)` | The project in the Slicer web app |
| `sourcesAvailable` _(required)_ | `boolean` | Sources are kept 7 days after the montage |
| `createdAt` _(required)_ | `string` | ISO 8601 date-time, UTC |
| `finishedAt` _(required)_ | `string \| null` |     |
| `stages` _(required)_ | `object[]` |     |
| `stages[].stage` _(required)_ | `string` |     |
| `stages[].status` _(required)_ | `string` | — `pending` · `running` · `succeeded` · `failed` · `skipped` |
| `stages[].inRun` _(required)_ | `boolean` | Part of the current run (not reused from a previous one) |
| `stages[].startedAt` _(required)_ | `string \| null` |     |
| `stages[].finishedAt` _(required)_ | `string \| null` |     |
| `stages[].durationSec` _(required)_ | `number \| null` |     |
| `stages[].progress` _(required)_ | `integer` |     |
| `stages[].tokens` _(required)_ | `integer` | Share of the run price |
| `run` _(required)_ | `object` |     |
| `run.kind` _(required)_ | `string` |     |
| `run.status` _(required)_ | `string` |     |
| `run.tokens` _(required)_ | `integer` |     |
| `run.tokensState` _(required)_ | `string` | — `free` · `reserved` · `charged` · `refunded` |
| `run.queuedAt` _(required)_ | `string` | ISO 8601 date-time, UTC |
| `run.startedAt` _(required)_ | `string \| null` |     |
| `run.finishedAt` _(required)_ | `string \| null` |     |
| `versions` _(required)_ | `ProjectVersion[]` |     |

Response example:

```json
{
  "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",
    "stages": [
      {
        "stage": "string",
        "status": "pending",
        "inRun": true,
        "startedAt": "2026-10-16T09:30:00Z",
        "finishedAt": "2026-10-16T09:30:00Z",
        "durationSec": 42.5,
        "progress": 0.5,
        "tokens": 24
      }
    ],
    "run": {
      "kind": "string",
      "status": "string",
      "tokens": 24,
      "tokensState": "free",
      "queuedAt": "2026-10-16T09:30:00Z",
      "startedAt": "2026-10-16T09:30:00Z",
      "finishedAt": "2026-10-16T09:30:00Z"
    },
    "versions": [
      {
        "id": 42,
        "version": null,
        "idea": "string",
        "durationSec": 42.5,
        "current": true,
        "awaitingChoice": true,
        "hasFile": true,
        "createdAt": "2026-10-16T09:30:00Z"
      }
    ]
  }
}
```

## Rename a project

`PATCH /v1/projects/{id}`

- **Scopes:** `projects:write`
- **Rate limit:** default

### Path parameters

| Field | Type | Description |
| --- | --- | --- |
| `id` _(required)_ | `integer` | — ≥ 1 |

### Request body `RenameRequest`

| Field | Type | Description |
| --- | --- | --- |
| `title` _(required)_ | `string` | — ≤ 120 |

### Request

**curl**

```bash
curl -X PATCH "https://api.slicer.media/v1/projects/42" \
  -H "Authorization: Bearer $SLICER_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"title":"Монтаж за 3 минуты"}'
```

**JavaScript**

```js
const response = await fetch('https://api.slicer.media/v1/projects/42', {
  method: 'PATCH',
  headers: {
    Authorization: `Bearer ${process.env.SLICER_TOKEN}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    "title": "Монтаж за 3 минуты"
  }),
});
const { ok, data, error } = await response.json();
```

**Python**

```python
import os
import requests

response = requests.patch(
    "https://api.slicer.media/v1/projects/42",
    headers={"Authorization": f"Bearer {os.environ['SLICER_TOKEN']}"},
    json={
        "title": "Монтаж за 3 минуты",
    },
)
body = response.json()
```

### Response

**Response fields: `Project` (26)**

| Field | Type | Description |
| --- | --- | --- |
| `id` _(required)_ | `integer` |     |
| `title` _(required)_ | `string` |     |
| `status` _(required)_ | `string` | `draft` — an “edit it myself” project before its first export: edit it with operations, then export — `queued` · `processing` · `succeeded` · `failed` · `cancelled` · `draft` |
| `mode` _(required)_ | `string` | `ai` — edited by the AI director; `manual` — materials on the timeline as is, free (only AI operations cost) — `ai` · `manual` |
| `stage` _(required)_ | `string \| null` |     |
| `progress` _(required)_ | `integer` | Progress of the current run, % |
| `client` _(required)_ | `string \| null` | Where it was created: web, mobile, api, mcp |
| `settings` _(required)_ | `object` | Montage settings (see MontageSettingsInput) |
| `brief` _(required)_ | `string \| null` |     |
| `isFree` _(required)_ | `boolean` |     |
| `watermark` _(required)_ | `boolean` |     |
| `tokensCost` _(required)_ | `integer` |     |
| `error` _(required)_ | `object \| null` |     |
| `error.code` _(required)_ | `string` |     |
| `error.message` _(required)_ | `string` |     |
| `durationSec` _(required)_ | `number \| null` |     |
| `versionChoice` _(required)_ | `integer[] \| null` | Version ids awaiting your choice; a montage returns one version, so usually `null` |
| `result` _(required)_ | `object \| null` |     |
| `result.url` _(required)_ | `string (uri)` | Download with the token (Range supported) |
| `result.posterUrl` _(required)_ | `string (uri) \| null` |     |
| `result.container` _(required)_ | `string` |     |
| `result.info` _(required)_ | `object \| null` |     |
| `appUrl` _(required)_ | `string (uri)` | The project in the Slicer web app |
| `sourcesAvailable` _(required)_ | `boolean` | Sources are kept 7 days after the montage |
| `createdAt` _(required)_ | `string` | ISO 8601 date-time, UTC |
| `finishedAt` _(required)_ | `string \| null` |     |

Response example:

```json
{
  "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"
  }
}
```

## Delete a project and its files

`DELETE /v1/projects/{id}`

- **Scopes:** `projects:write`
- **Rate limit:** default

### Path parameters

| Field | Type | Description |
| --- | --- | --- |
| `id` _(required)_ | `integer` | — ≥ 1 |

### Request

**curl**

```bash
curl -X DELETE "https://api.slicer.media/v1/projects/42" \
  -H "Authorization: Bearer $SLICER_TOKEN"
```

**JavaScript**

```js
const response = await fetch('https://api.slicer.media/v1/projects/42', {
  method: 'DELETE',
  headers: {
    Authorization: `Bearer ${process.env.SLICER_TOKEN}`,
  },
});
const { ok, data, error } = await response.json();
```

**Python**

```python
import os
import requests

response = requests.delete(
    "https://api.slicer.media/v1/projects/42",
    headers={"Authorization": f"Bearer {os.environ['SLICER_TOKEN']}"},
)
body = response.json()
```

### Response

**Response fields: `object` (2)**

| Field | Type | Description |
| --- | --- | --- |
| `id` _(required)_ | `integer` |     |
| `deleted` _(required)_ | `boolean` |     |

Response example:

```json
{
  "ok": true,
  "data": {
    "id": 42,
    "deleted": true
  }
}
```

## Runs

### Rebuild with new settings

`POST /v1/projects/{id}/remount`

- **Scopes:** `montage:run`
- **Rate limit:** paid operations
- **Price:** _spends tokens_

> **Important.** Without `confirmCost` it returns the price (`Quote`) and starts nothing.

#### Path parameters

| Field | Type | Description |
| --- | --- | --- |
| `id` _(required)_ | `integer` | — ≥ 1 |

#### Request body `RemountRequest`

| Field | Type | Description |
| --- | --- | --- |
| `settings` _(required)_ | `MontageSettingsInput` |     |
| `brief` | `string` | — ≤ 600 |
| `confirmCost` | `integer` | Price you agree to pay. Without it the response is a `Quote` and nothing starts |

#### Request

**curl**

```bash
curl -X POST "https://api.slicer.media/v1/projects/42/remount" \
  -H "Authorization: Bearer $SLICER_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"settings":{"templateId":"string","fontId":"string","colorId":"string","subtitles":{"enabled":true,"design":"dynamic","size":"small","position":"auto","speakerColors":true,"highlight":"auto","animation":"auto","words":"auto","letterCase":"auto"},"params":{"emoji":true,"soundEffects":true,"mediaCards":true,"plaques":true,"cameraMotion":true,"introZoom":true,"hook":true},"smartCut":true,"targetDurationSec":15,"language":"auto","direction":{"mode":"auto","pace":"calm","effects":"off","narration":true,"voice":"warm"}}}'
```

**JavaScript**

```js
const response = await fetch('https://api.slicer.media/v1/projects/42/remount', {
  method: 'POST',
  headers: {
    Authorization: `Bearer ${process.env.SLICER_TOKEN}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    "settings": {
      "templateId": "string",
      "fontId": "string",
      "colorId": "string",
      "subtitles": {
        "enabled": true,
        "design": "dynamic",
        "size": "small",
        "position": "auto",
        "speakerColors": true,
        "highlight": "auto",
        "animation": "auto",
        "words": "auto",
        "letterCase": "auto"
      },
      "params": {
        "emoji": true,
        "soundEffects": true,
        "mediaCards": true,
        "plaques": true,
        "cameraMotion": true,
        "introZoom": true,
        "hook": true
      },
      "smartCut": true,
      "targetDurationSec": 15,
      "language": "auto",
      "direction": {
        "mode": "auto",
        "pace": "calm",
        "effects": "off",
        "narration": true,
        "voice": "warm"
      }
    }
  }),
});
const { ok, data, error } = await response.json();
```

**Python**

```python
import os
import requests

response = requests.post(
    "https://api.slicer.media/v1/projects/42/remount",
    headers={"Authorization": f"Bearer {os.environ['SLICER_TOKEN']}"},
    json={
        "settings": {
            "templateId": "string",
            "fontId": "string",
            "colorId": "string",
            "subtitles": {
                "enabled": True,
                "design": "dynamic",
                "size": "small",
                "position": "auto",
                "speakerColors": True,
                "highlight": "auto",
                "animation": "auto",
                "words": "auto",
                "letterCase": "auto",
            },
            "params": {
                "emoji": True,
                "soundEffects": True,
                "mediaCards": True,
                "plaques": True,
                "cameraMotion": True,
                "introZoom": True,
                "hook": True,
            },
            "smartCut": True,
            "targetDurationSec": 15,
            "language": "auto",
            "direction": {
                "mode": "auto",
                "pace": "calm",
                "effects": "off",
                "narration": True,
                "voice": "warm",
            },
        },
    },
)
body = response.json()
```

#### Response

One of: Quote, ProjectDetails

**Response fields: `Quote` (8)**

| Field | Type | Description |
| --- | --- | --- |
| `status` _(required)_ | `"quote"` |     |
| `cost` _(required)_ | `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 |
| `free` _(required)_ | `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` |
| `available` _(required)_ | `integer` | Tokens available now |
| `enough` _(required)_ | `boolean` | Enough tokens to run |
| `spendLimitLeft` _(required)_ | `integer \| null` | What is left of the token's monthly spend limit; `null` — no limit |

**Response fields: `ProjectDetails` (44)**

| Field | Type | Description |
| --- | --- | --- |
| `id` _(required)_ | `integer` |     |
| `title` _(required)_ | `string` |     |
| `status` _(required)_ | `string` | `draft` — an “edit it myself” project before its first export: edit it with operations, then export — `queued` · `processing` · `succeeded` · `failed` · `cancelled` · `draft` |
| `mode` _(required)_ | `string` | `ai` — edited by the AI director; `manual` — materials on the timeline as is, free (only AI operations cost) — `ai` · `manual` |
| `stage` _(required)_ | `string \| null` |     |
| `progress` _(required)_ | `integer` | Progress of the current run, % |
| `client` _(required)_ | `string \| null` | Where it was created: web, mobile, api, mcp |
| `settings` _(required)_ | `object` | Montage settings (see MontageSettingsInput) |
| `brief` _(required)_ | `string \| null` |     |
| `isFree` _(required)_ | `boolean` |     |
| `watermark` _(required)_ | `boolean` |     |
| `tokensCost` _(required)_ | `integer` |     |
| `error` _(required)_ | `object \| null` |     |
| `error.code` _(required)_ | `string` |     |
| `error.message` _(required)_ | `string` |     |
| `durationSec` _(required)_ | `number \| null` |     |
| `versionChoice` _(required)_ | `integer[] \| null` | Version ids awaiting your choice; a montage returns one version, so usually `null` |
| `result` _(required)_ | `object \| null` |     |
| `result.url` _(required)_ | `string (uri)` | Download with the token (Range supported) |
| `result.posterUrl` _(required)_ | `string (uri) \| null` |     |
| `result.container` _(required)_ | `string` |     |
| `result.info` _(required)_ | `object \| null` |     |
| `appUrl` _(required)_ | `string (uri)` | The project in the Slicer web app |
| `sourcesAvailable` _(required)_ | `boolean` | Sources are kept 7 days after the montage |
| `createdAt` _(required)_ | `string` | ISO 8601 date-time, UTC |
| `finishedAt` _(required)_ | `string \| null` |     |
| `stages` _(required)_ | `object[]` |     |
| `stages[].stage` _(required)_ | `string` |     |
| `stages[].status` _(required)_ | `string` | — `pending` · `running` · `succeeded` · `failed` · `skipped` |
| `stages[].inRun` _(required)_ | `boolean` | Part of the current run (not reused from a previous one) |
| `stages[].startedAt` _(required)_ | `string \| null` |     |
| `stages[].finishedAt` _(required)_ | `string \| null` |     |
| `stages[].durationSec` _(required)_ | `number \| null` |     |
| `stages[].progress` _(required)_ | `integer` |     |
| `stages[].tokens` _(required)_ | `integer` | Share of the run price |
| `run` _(required)_ | `object` |     |
| `run.kind` _(required)_ | `string` |     |
| `run.status` _(required)_ | `string` |     |
| `run.tokens` _(required)_ | `integer` |     |
| `run.tokensState` _(required)_ | `string` | — `free` · `reserved` · `charged` · `refunded` |
| `run.queuedAt` _(required)_ | `string` | ISO 8601 date-time, UTC |
| `run.startedAt` _(required)_ | `string \| null` |     |
| `run.finishedAt` _(required)_ | `string \| null` |     |
| `versions` _(required)_ | `ProjectVersion[]` |     |

Response example:

```json
{
  "ok": true,
  "data": {
    "status": "quote",
    "cost": 24,
    "free": true,
    "estimate": 1.5,
    "atCost": true,
    "available": 240,
    "enough": true,
    "spendLimitLeft": 200
  }
}
```

### Retry a failed run

`POST /v1/projects/{id}/retry`

- **Scopes:** `montage:run`
- **Rate limit:** paid operations
- **Price:** _spends tokens_

> **Important.** Without `confirmCost` it returns the price (`Quote`) and starts nothing.

#### Path parameters

| Field | Type | Description |
| --- | --- | --- |
| `id` _(required)_ | `integer` | — ≥ 1 |

#### Request body `RetryRequest`

| Field | Type | Description |
| --- | --- | --- |
| `confirmCost` | `integer` | Price you agree to pay. Without it the response is a `Quote` and nothing starts |

#### Request

**curl**

```bash
curl -X POST "https://api.slicer.media/v1/projects/42/retry" \
  -H "Authorization: Bearer $SLICER_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{}'
```

**JavaScript**

```js
const response = await fetch('https://api.slicer.media/v1/projects/42/retry', {
  method: 'POST',
  headers: {
    Authorization: `Bearer ${process.env.SLICER_TOKEN}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({}),
});
const { ok, data, error } = await response.json();
```

**Python**

```python
import os
import requests

response = requests.post(
    "https://api.slicer.media/v1/projects/42/retry",
    headers={"Authorization": f"Bearer {os.environ['SLICER_TOKEN']}"},
    json={},
)
body = response.json()
```

#### Response

One of: Quote, ProjectDetails

**Response fields: `Quote` (8)**

| Field | Type | Description |
| --- | --- | --- |
| `status` _(required)_ | `"quote"` |     |
| `cost` _(required)_ | `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 |
| `free` _(required)_ | `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` |
| `available` _(required)_ | `integer` | Tokens available now |
| `enough` _(required)_ | `boolean` | Enough tokens to run |
| `spendLimitLeft` _(required)_ | `integer \| null` | What is left of the token's monthly spend limit; `null` — no limit |

**Response fields: `ProjectDetails` (44)**

| Field | Type | Description |
| --- | --- | --- |
| `id` _(required)_ | `integer` |     |
| `title` _(required)_ | `string` |     |
| `status` _(required)_ | `string` | `draft` — an “edit it myself” project before its first export: edit it with operations, then export — `queued` · `processing` · `succeeded` · `failed` · `cancelled` · `draft` |
| `mode` _(required)_ | `string` | `ai` — edited by the AI director; `manual` — materials on the timeline as is, free (only AI operations cost) — `ai` · `manual` |
| `stage` _(required)_ | `string \| null` |     |
| `progress` _(required)_ | `integer` | Progress of the current run, % |
| `client` _(required)_ | `string \| null` | Where it was created: web, mobile, api, mcp |
| `settings` _(required)_ | `object` | Montage settings (see MontageSettingsInput) |
| `brief` _(required)_ | `string \| null` |     |
| `isFree` _(required)_ | `boolean` |     |
| `watermark` _(required)_ | `boolean` |     |
| `tokensCost` _(required)_ | `integer` |     |
| `error` _(required)_ | `object \| null` |     |
| `error.code` _(required)_ | `string` |     |
| `error.message` _(required)_ | `string` |     |
| `durationSec` _(required)_ | `number \| null` |     |
| `versionChoice` _(required)_ | `integer[] \| null` | Version ids awaiting your choice; a montage returns one version, so usually `null` |
| `result` _(required)_ | `object \| null` |     |
| `result.url` _(required)_ | `string (uri)` | Download with the token (Range supported) |
| `result.posterUrl` _(required)_ | `string (uri) \| null` |     |
| `result.container` _(required)_ | `string` |     |
| `result.info` _(required)_ | `object \| null` |     |
| `appUrl` _(required)_ | `string (uri)` | The project in the Slicer web app |
| `sourcesAvailable` _(required)_ | `boolean` | Sources are kept 7 days after the montage |
| `createdAt` _(required)_ | `string` | ISO 8601 date-time, UTC |
| `finishedAt` _(required)_ | `string \| null` |     |
| `stages` _(required)_ | `object[]` |     |
| `stages[].stage` _(required)_ | `string` |     |
| `stages[].status` _(required)_ | `string` | — `pending` · `running` · `succeeded` · `failed` · `skipped` |
| `stages[].inRun` _(required)_ | `boolean` | Part of the current run (not reused from a previous one) |
| `stages[].startedAt` _(required)_ | `string \| null` |     |
| `stages[].finishedAt` _(required)_ | `string \| null` |     |
| `stages[].durationSec` _(required)_ | `number \| null` |     |
| `stages[].progress` _(required)_ | `integer` |     |
| `stages[].tokens` _(required)_ | `integer` | Share of the run price |
| `run` _(required)_ | `object` |     |
| `run.kind` _(required)_ | `string` |     |
| `run.status` _(required)_ | `string` |     |
| `run.tokens` _(required)_ | `integer` |     |
| `run.tokensState` _(required)_ | `string` | — `free` · `reserved` · `charged` · `refunded` |
| `run.queuedAt` _(required)_ | `string` | ISO 8601 date-time, UTC |
| `run.startedAt` _(required)_ | `string \| null` |     |
| `run.finishedAt` _(required)_ | `string \| null` |     |
| `versions` _(required)_ | `ProjectVersion[]` |     |

Response example:

```json
{
  "ok": true,
  "data": {
    "status": "quote",
    "cost": 24,
    "free": true,
    "estimate": 1.5,
    "atCost": true,
    "available": 240,
    "enough": true,
    "spendLimitLeft": 200
  }
}
```

### Stop the run

`POST /v1/projects/{id}/cancel`

- **Scopes:** `projects:write`
- **Rate limit:** default

#### Path parameters

| Field | Type | Description |
| --- | --- | --- |
| `id` _(required)_ | `integer` | — ≥ 1 |

#### Request

**curl**

```bash
curl -X POST "https://api.slicer.media/v1/projects/42/cancel" \
  -H "Authorization: Bearer $SLICER_TOKEN"
```

**JavaScript**

```js
const response = await fetch('https://api.slicer.media/v1/projects/42/cancel', {
  method: 'POST',
  headers: {
    Authorization: `Bearer ${process.env.SLICER_TOKEN}`,
  },
});
const { ok, data, error } = await response.json();
```

**Python**

```python
import os
import requests

response = requests.post(
    "https://api.slicer.media/v1/projects/42/cancel",
    headers={"Authorization": f"Bearer {os.environ['SLICER_TOKEN']}"},
)
body = response.json()
```

#### Response

**Response fields: `ProjectDetails` (44)**

| Field | Type | Description |
| --- | --- | --- |
| `id` _(required)_ | `integer` |     |
| `title` _(required)_ | `string` |     |
| `status` _(required)_ | `string` | `draft` — an “edit it myself” project before its first export: edit it with operations, then export — `queued` · `processing` · `succeeded` · `failed` · `cancelled` · `draft` |
| `mode` _(required)_ | `string` | `ai` — edited by the AI director; `manual` — materials on the timeline as is, free (only AI operations cost) — `ai` · `manual` |
| `stage` _(required)_ | `string \| null` |     |
| `progress` _(required)_ | `integer` | Progress of the current run, % |
| `client` _(required)_ | `string \| null` | Where it was created: web, mobile, api, mcp |
| `settings` _(required)_ | `object` | Montage settings (see MontageSettingsInput) |
| `brief` _(required)_ | `string \| null` |     |
| `isFree` _(required)_ | `boolean` |     |
| `watermark` _(required)_ | `boolean` |     |
| `tokensCost` _(required)_ | `integer` |     |
| `error` _(required)_ | `object \| null` |     |
| `error.code` _(required)_ | `string` |     |
| `error.message` _(required)_ | `string` |     |
| `durationSec` _(required)_ | `number \| null` |     |
| `versionChoice` _(required)_ | `integer[] \| null` | Version ids awaiting your choice; a montage returns one version, so usually `null` |
| `result` _(required)_ | `object \| null` |     |
| `result.url` _(required)_ | `string (uri)` | Download with the token (Range supported) |
| `result.posterUrl` _(required)_ | `string (uri) \| null` |     |
| `result.container` _(required)_ | `string` |     |
| `result.info` _(required)_ | `object \| null` |     |
| `appUrl` _(required)_ | `string (uri)` | The project in the Slicer web app |
| `sourcesAvailable` _(required)_ | `boolean` | Sources are kept 7 days after the montage |
| `createdAt` _(required)_ | `string` | ISO 8601 date-time, UTC |
| `finishedAt` _(required)_ | `string \| null` |     |
| `stages` _(required)_ | `object[]` |     |
| `stages[].stage` _(required)_ | `string` |     |
| `stages[].status` _(required)_ | `string` | — `pending` · `running` · `succeeded` · `failed` · `skipped` |
| `stages[].inRun` _(required)_ | `boolean` | Part of the current run (not reused from a previous one) |
| `stages[].startedAt` _(required)_ | `string \| null` |     |
| `stages[].finishedAt` _(required)_ | `string \| null` |     |
| `stages[].durationSec` _(required)_ | `number \| null` |     |
| `stages[].progress` _(required)_ | `integer` |     |
| `stages[].tokens` _(required)_ | `integer` | Share of the run price |
| `run` _(required)_ | `object` |     |
| `run.kind` _(required)_ | `string` |     |
| `run.status` _(required)_ | `string` |     |
| `run.tokens` _(required)_ | `integer` |     |
| `run.tokensState` _(required)_ | `string` | — `free` · `reserved` · `charged` · `refunded` |
| `run.queuedAt` _(required)_ | `string` | ISO 8601 date-time, UTC |
| `run.startedAt` _(required)_ | `string \| null` |     |
| `run.finishedAt` _(required)_ | `string \| null` |     |
| `versions` _(required)_ | `ProjectVersion[]` |     |

Response example:

```json
{
  "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",
    "stages": [
      {
        "stage": "string",
        "status": "pending",
        "inRun": true,
        "startedAt": "2026-10-16T09:30:00Z",
        "finishedAt": "2026-10-16T09:30:00Z",
        "durationSec": 42.5,
        "progress": 0.5,
        "tokens": 24
      }
    ],
    "run": {
      "kind": "string",
      "status": "string",
      "tokens": 24,
      "tokensState": "free",
      "queuedAt": "2026-10-16T09:30:00Z",
      "startedAt": "2026-10-16T09:30:00Z",
      "finishedAt": "2026-10-16T09:30:00Z"
    },
    "versions": [
      {
        "id": 42,
        "version": null,
        "idea": "string",
        "durationSec": 42.5,
        "current": true,
        "awaitingChoice": true,
        "hasFile": true,
        "createdAt": "2026-10-16T09:30:00Z"
      }
    ]
  }
}
```

## Versions and history

### Video versions

`GET /v1/projects/{id}/versions`

- **Scopes:** `projects:read`
- **Rate limit:** default

#### Path parameters

| Field | Type | Description |
| --- | --- | --- |
| `id` _(required)_ | `integer` | — ≥ 1 |

#### Request

**curl**

```bash
curl "https://api.slicer.media/v1/projects/42/versions" \
  -H "Authorization: Bearer $SLICER_TOKEN"
```

**JavaScript**

```js
const response = await fetch('https://api.slicer.media/v1/projects/42/versions', {
  headers: {
    Authorization: `Bearer ${process.env.SLICER_TOKEN}`,
  },
});
const { ok, data, error } = await response.json();
```

**Python**

```python
import os
import requests

response = requests.get(
    "https://api.slicer.media/v1/projects/42/versions",
    headers={"Authorization": f"Bearer {os.environ['SLICER_TOKEN']}"},
)
body = response.json()
```

#### Response

Response example:

```json
{
  "ok": true,
  "data": [
    {
      "id": 42,
      "version": 1,
      "idea": "string",
      "durationSec": 42.5,
      "current": true,
      "awaitingChoice": true,
      "hasFile": true,
      "createdAt": "2026-10-16T09:30:00Z"
    }
  ]
}
```

### Choose a version

`POST /v1/projects/{id}/versions/{versionId}/choose`

- **Scopes:** `projects:write`
- **Rate limit:** default

Makes a version current: one awaiting choice (`versionChoice`) or any version from the history whose file is kept (`hasFile`) — like «Make current» in the app. The previous video stays in the history.

#### Path parameters

| Field | Type | Description |
| --- | --- | --- |
| `id` _(required)_ | `integer` | — ≥ 1 |
| `versionId` _(required)_ | `integer` | — ≥ 1 |

#### Request

**curl**

```bash
curl -X POST "https://api.slicer.media/v1/projects/42/versions/42/choose" \
  -H "Authorization: Bearer $SLICER_TOKEN"
```

**JavaScript**

```js
const response = await fetch('https://api.slicer.media/v1/projects/42/versions/42/choose', {
  method: 'POST',
  headers: {
    Authorization: `Bearer ${process.env.SLICER_TOKEN}`,
  },
});
const { ok, data, error } = await response.json();
```

**Python**

```python
import os
import requests

response = requests.post(
    "https://api.slicer.media/v1/projects/42/versions/42/choose",
    headers={"Authorization": f"Bearer {os.environ['SLICER_TOKEN']}"},
)
body = response.json()
```

#### Response

**Response fields: `ProjectDetails` (44)**

| Field | Type | Description |
| --- | --- | --- |
| `id` _(required)_ | `integer` |     |
| `title` _(required)_ | `string` |     |
| `status` _(required)_ | `string` | `draft` — an “edit it myself” project before its first export: edit it with operations, then export — `queued` · `processing` · `succeeded` · `failed` · `cancelled` · `draft` |
| `mode` _(required)_ | `string` | `ai` — edited by the AI director; `manual` — materials on the timeline as is, free (only AI operations cost) — `ai` · `manual` |
| `stage` _(required)_ | `string \| null` |     |
| `progress` _(required)_ | `integer` | Progress of the current run, % |
| `client` _(required)_ | `string \| null` | Where it was created: web, mobile, api, mcp |
| `settings` _(required)_ | `object` | Montage settings (see MontageSettingsInput) |
| `brief` _(required)_ | `string \| null` |     |
| `isFree` _(required)_ | `boolean` |     |
| `watermark` _(required)_ | `boolean` |     |
| `tokensCost` _(required)_ | `integer` |     |
| `error` _(required)_ | `object \| null` |     |
| `error.code` _(required)_ | `string` |     |
| `error.message` _(required)_ | `string` |     |
| `durationSec` _(required)_ | `number \| null` |     |
| `versionChoice` _(required)_ | `integer[] \| null` | Version ids awaiting your choice; a montage returns one version, so usually `null` |
| `result` _(required)_ | `object \| null` |     |
| `result.url` _(required)_ | `string (uri)` | Download with the token (Range supported) |
| `result.posterUrl` _(required)_ | `string (uri) \| null` |     |
| `result.container` _(required)_ | `string` |     |
| `result.info` _(required)_ | `object \| null` |     |
| `appUrl` _(required)_ | `string (uri)` | The project in the Slicer web app |
| `sourcesAvailable` _(required)_ | `boolean` | Sources are kept 7 days after the montage |
| `createdAt` _(required)_ | `string` | ISO 8601 date-time, UTC |
| `finishedAt` _(required)_ | `string \| null` |     |
| `stages` _(required)_ | `object[]` |     |
| `stages[].stage` _(required)_ | `string` |     |
| `stages[].status` _(required)_ | `string` | — `pending` · `running` · `succeeded` · `failed` · `skipped` |
| `stages[].inRun` _(required)_ | `boolean` | Part of the current run (not reused from a previous one) |
| `stages[].startedAt` _(required)_ | `string \| null` |     |
| `stages[].finishedAt` _(required)_ | `string \| null` |     |
| `stages[].durationSec` _(required)_ | `number \| null` |     |
| `stages[].progress` _(required)_ | `integer` |     |
| `stages[].tokens` _(required)_ | `integer` | Share of the run price |
| `run` _(required)_ | `object` |     |
| `run.kind` _(required)_ | `string` |     |
| `run.status` _(required)_ | `string` |     |
| `run.tokens` _(required)_ | `integer` |     |
| `run.tokensState` _(required)_ | `string` | — `free` · `reserved` · `charged` · `refunded` |
| `run.queuedAt` _(required)_ | `string` | ISO 8601 date-time, UTC |
| `run.startedAt` _(required)_ | `string \| null` |     |
| `run.finishedAt` _(required)_ | `string \| null` |     |
| `versions` _(required)_ | `ProjectVersion[]` |     |

Response example:

```json
{
  "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",
    "stages": [
      {
        "stage": "string",
        "status": "pending",
        "inRun": true,
        "startedAt": "2026-10-16T09:30:00Z",
        "finishedAt": "2026-10-16T09:30:00Z",
        "durationSec": 42.5,
        "progress": 0.5,
        "tokens": 24
      }
    ],
    "run": {
      "kind": "string",
      "status": "string",
      "tokens": 24,
      "tokensState": "free",
      "queuedAt": "2026-10-16T09:30:00Z",
      "startedAt": "2026-10-16T09:30:00Z",
      "finishedAt": "2026-10-16T09:30:00Z"
    },
    "versions": [
      {
        "id": 42,
        "version": null,
        "idea": "string",
        "durationSec": 42.5,
        "current": true,
        "awaitingChoice": true,
        "hasFile": true,
        "createdAt": "2026-10-16T09:30:00Z"
      }
    ]
  }
}
```

### Project history

`GET /v1/projects/{id}/history`

- **Scopes:** `projects:read`
- **Rate limit:** default

#### Path parameters

| Field | Type | Description |
| --- | --- | --- |
| `id` _(required)_ | `integer` | — ≥ 1 |

#### Request

**curl**

```bash
curl "https://api.slicer.media/v1/projects/42/history" \
  -H "Authorization: Bearer $SLICER_TOKEN"
```

**JavaScript**

```js
const response = await fetch('https://api.slicer.media/v1/projects/42/history', {
  headers: {
    Authorization: `Bearer ${process.env.SLICER_TOKEN}`,
  },
});
const { ok, data, error } = await response.json();
```

**Python**

```python
import os
import requests

response = requests.get(
    "https://api.slicer.media/v1/projects/42/history",
    headers={"Authorization": f"Bearer {os.environ['SLICER_TOKEN']}"},
)
body = response.json()
```

#### Response

Response example:

```json
{
  "ok": true,
  "data": [
    {
      "id": 42,
      "kind": "string",
      "status": "string",
      "title": "Монтаж за 3 минуты",
      "version": 1,
      "createdAt": "2026-10-16T09:30:00Z"
    }
  ]
}
```

## Files

### Download the video

`GET /v1/projects/{id}/result`

- **Scopes:** `projects:read`
- **Rate limit:** default

Range requests are supported. `?version={versionId}` — a kept version file.

#### Path parameters

| Field | Type | Description |
| --- | --- | --- |
| `id` _(required)_ | `integer` | — ≥ 1 |

#### Query parameters

| Field | Type | Description |
| --- | --- | --- |
| `version` | `integer` | — ≥ 1 |

#### Request

**curl**

```bash
curl "https://api.slicer.media/v1/projects/42/result" \
  -H "Authorization: Bearer $SLICER_TOKEN" \
  -o video.mp4
```

**JavaScript**

```js
import { writeFile } from 'node:fs/promises';

const response = await fetch('https://api.slicer.media/v1/projects/42/result', {
  headers: {
    Authorization: `Bearer ${process.env.SLICER_TOKEN}`,
  },
});
await writeFile('video.mp4', Buffer.from(await response.arrayBuffer()));
```

**Python**

```python
import os
import requests

response = requests.get(
    "https://api.slicer.media/v1/projects/42/result",
    headers={"Authorization": f"Bearer {os.environ['SLICER_TOKEN']}"},
    stream=True,
)
with open("video.mp4", "wb") as out:
    for chunk in response.iter_content(chunk_size=1 << 20):
        out.write(chunk)
```

#### Response

A file (`application/octet-stream`); Range requests are supported.

### Download the cover (JPEG)

`GET /v1/projects/{id}/poster`

- **Scopes:** `projects:read`
- **Rate limit:** default

#### Path parameters

| Field | Type | Description |
| --- | --- | --- |
| `id` _(required)_ | `integer` | — ≥ 1 |

#### Request

**curl**

```bash
curl "https://api.slicer.media/v1/projects/42/poster" \
  -H "Authorization: Bearer $SLICER_TOKEN" \
  -o poster.jpg
```

**JavaScript**

```js
import { writeFile } from 'node:fs/promises';

const response = await fetch('https://api.slicer.media/v1/projects/42/poster', {
  headers: {
    Authorization: `Bearer ${process.env.SLICER_TOKEN}`,
  },
});
await writeFile('poster.jpg', Buffer.from(await response.arrayBuffer()));
```

**Python**

```python
import os
import requests

response = requests.get(
    "https://api.slicer.media/v1/projects/42/poster",
    headers={"Authorization": f"Bearer {os.environ['SLICER_TOKEN']}"},
    stream=True,
)
with open("poster.jpg", "wb") as out:
    for chunk in response.iter_content(chunk_size=1 << 20):
        out.write(chunk)
```

#### Response

A file (`application/octet-stream`); Range requests are supported.

## Schemas

`Project`

| Field | Type | Description |
| --- | --- | --- |
| `id` _(required)_ | `integer` |     |
| `title` _(required)_ | `string` |     |
| `status` _(required)_ | `string` | `draft` — an “edit it myself” project before its first export: edit it with operations, then export — `queued` · `processing` · `succeeded` · `failed` · `cancelled` · `draft` |
| `mode` _(required)_ | `string` | `ai` — edited by the AI director; `manual` — materials on the timeline as is, free (only AI operations cost) — `ai` · `manual` |
| `stage` _(required)_ | `string \| null` |     |
| `progress` _(required)_ | `integer` | Progress of the current run, % |
| `client` _(required)_ | `string \| null` | Where it was created: web, mobile, api, mcp |
| `settings` _(required)_ | `object` | Montage settings (see MontageSettingsInput) |
| `brief` _(required)_ | `string \| null` |     |
| `isFree` _(required)_ | `boolean` |     |
| `watermark` _(required)_ | `boolean` |     |
| `tokensCost` _(required)_ | `integer` |     |
| `error` _(required)_ | `object \| null` |     |
| `error.code` _(required)_ | `string` |     |
| `error.message` _(required)_ | `string` |     |
| `durationSec` _(required)_ | `number \| null` |     |
| `versionChoice` _(required)_ | `integer[] \| null` | Version ids awaiting your choice; a montage returns one version, so usually `null` |
| `result` _(required)_ | `object \| null` |     |
| `result.url` _(required)_ | `string (uri)` | Download with the token (Range supported) |
| `result.posterUrl` _(required)_ | `string (uri) \| null` |     |
| `result.container` _(required)_ | `string` |     |
| `result.info` _(required)_ | `object \| null` |     |
| `appUrl` _(required)_ | `string (uri)` | The project in the Slicer web app |
| `sourcesAvailable` _(required)_ | `boolean` | Sources are kept 7 days after the montage |
| `createdAt` _(required)_ | `string` | ISO 8601 date-time, UTC |
| `finishedAt` _(required)_ | `string \| null` |     |

`ProjectDetails`

| Field | Type | Description |
| --- | --- | --- |
| `id` _(required)_ | `integer` |     |
| `title` _(required)_ | `string` |     |
| `status` _(required)_ | `string` | `draft` — an “edit it myself” project before its first export: edit it with operations, then export — `queued` · `processing` · `succeeded` · `failed` · `cancelled` · `draft` |
| `mode` _(required)_ | `string` | `ai` — edited by the AI director; `manual` — materials on the timeline as is, free (only AI operations cost) — `ai` · `manual` |
| `stage` _(required)_ | `string \| null` |     |
| `progress` _(required)_ | `integer` | Progress of the current run, % |
| `client` _(required)_ | `string \| null` | Where it was created: web, mobile, api, mcp |
| `settings` _(required)_ | `object` | Montage settings (see MontageSettingsInput) |
| `brief` _(required)_ | `string \| null` |     |
| `isFree` _(required)_ | `boolean` |     |
| `watermark` _(required)_ | `boolean` |     |
| `tokensCost` _(required)_ | `integer` |     |
| `error` _(required)_ | `object \| null` |     |
| `error.code` _(required)_ | `string` |     |
| `error.message` _(required)_ | `string` |     |
| `durationSec` _(required)_ | `number \| null` |     |
| `versionChoice` _(required)_ | `integer[] \| null` | Version ids awaiting your choice; a montage returns one version, so usually `null` |
| `result` _(required)_ | `object \| null` |     |
| `result.url` _(required)_ | `string (uri)` | Download with the token (Range supported) |
| `result.posterUrl` _(required)_ | `string (uri) \| null` |     |
| `result.container` _(required)_ | `string` |     |
| `result.info` _(required)_ | `object \| null` |     |
| `appUrl` _(required)_ | `string (uri)` | The project in the Slicer web app |
| `sourcesAvailable` _(required)_ | `boolean` | Sources are kept 7 days after the montage |
| `createdAt` _(required)_ | `string` | ISO 8601 date-time, UTC |
| `finishedAt` _(required)_ | `string \| null` |     |
| `stages` _(required)_ | `object[]` |     |
| `stages[].stage` _(required)_ | `string` |     |
| `stages[].status` _(required)_ | `string` | — `pending` · `running` · `succeeded` · `failed` · `skipped` |
| `stages[].inRun` _(required)_ | `boolean` | Part of the current run (not reused from a previous one) |
| `stages[].startedAt` _(required)_ | `string \| null` |     |
| `stages[].finishedAt` _(required)_ | `string \| null` |     |
| `stages[].durationSec` _(required)_ | `number \| null` |     |
| `stages[].progress` _(required)_ | `integer` |     |
| `stages[].tokens` _(required)_ | `integer` | Share of the run price |
| `run` _(required)_ | `object` |     |
| `run.kind` _(required)_ | `string` |     |
| `run.status` _(required)_ | `string` |     |
| `run.tokens` _(required)_ | `integer` |     |
| `run.tokensState` _(required)_ | `string` | — `free` · `reserved` · `charged` · `refunded` |
| `run.queuedAt` _(required)_ | `string` | ISO 8601 date-time, UTC |
| `run.startedAt` _(required)_ | `string \| null` |     |
| `run.finishedAt` _(required)_ | `string \| null` |     |
| `versions` _(required)_ | `ProjectVersion[]` |     |

`ProjectVersion`

| Field | Type | Description |
| --- | --- | --- |
| `id` _(required)_ | `integer` | Version id (history event) |
| `version` _(required)_ | `integer \| null` |     |
| `idea` _(required)_ | `string \| null` | What makes this version different |
| `durationSec` _(required)_ | `number \| null` |     |
| `current` _(required)_ | `boolean` |     |
| `awaitingChoice` _(required)_ | `boolean` | Offered for choice after the last montage |
| `hasFile` _(required)_ | `boolean` | The file is kept (last 10 versions) |
| `createdAt` _(required)_ | `string` | ISO 8601 date-time, UTC |

`CreateProjectRequest`

| Field | Type | Description |
| --- | --- | --- |
| `uploadIds` _(required)_ | `string (uuid)[]` | Ready `source` uploads in material order — 1–20 |
| `mode` | `string` | `ai` (default) — the AI director edits the video, paid; `manual` — materials go to the timeline in order, the project is a free `draft` you edit and export yourself — `ai` · `manual` |
| `format` | `string` | Frame format of a `manual` project (default 9:16) — `9:16` · `4:5` · `1:1` · `16:9` |
| `settings` | `MontageSettingsInput` |     |
| `title` | `string` | — ≤ 120 |
| `brief` | `string` | Wishes for the AI director — ≤ 600 |
| `confirmCost` | `integer` | Price you agree to pay. Without it the response is a `Quote` and nothing starts |

`HistoryEvent`

| Field | Type | Description |
| --- | --- | --- |
| `id` _(required)_ | `integer` |     |
| `kind` _(required)_ | `string` |     |
| `status` _(required)_ | `string` |     |
| `title` _(required)_ | `string \| null` |     |
| `version` _(required)_ | `integer \| null` |     |
| `createdAt` _(required)_ | `string` | ISO 8601 date-time, UTC |
