---
title: "Create a video from a template"
description: "Queues a render of a template and spends credits equal to the video's\nlength. Variables you leave out (`nama_usaha`, `warna_utama`,\n`warna_latar`, `kontak`) come from the workspace brand kit. Send an\n`idempotency_key` so a retried request does not create a second video.\nNeeds the `videos:create` permission.\n"
---

> Documentation Index
> Fetch the complete documentation index at: https://docs.mosio.me/llms.txt
> Use this file to discover all available pages before exploring further.

Path: Mosio REST API › Videos

`POST /v1/videos`

Queues a render of a template and spends credits equal to the video's
length. Variables you leave out (`nama_usaha`, `warna_utama`,
`warna_latar`, `kontak`) come from the workspace brand kit. Send an
`idempotency_key` so a retried request does not create a second video.
Needs the `videos:create` permission.

## Authentication

- `apiKey`, apiKey, in header, header `x-api-key`

## Request body

_Required._

- `template` (`createVideo.template`; string, required) — Template id with its version, such as `promo-produk@1`. See the template reference.
- `variables` (`createVideo.variables`; map<string>, optional) — Template variables. Every value is a string; colours are `#RRGGBB`.
  - default `{}`
- `output` (`createVideo.output`; object, optional) — Output size and frame rate. The size must be one the template supports.
  - `width` (`createVideo.output.width`; integer, optional)
    - default `1080`
  - `height` (`createVideo.output.height`; integer, optional)
    - default `1920`
  - `fps` (`createVideo.output.fps`; integer, optional)
    - one of `24`, `25`, `30`; default `30`
- `idempotency_key` (`createVideo.idempotency_key`; string, optional) — Send the same key again to get the same video instead of a second one.
  - minLength 1; maxLength 200

## Example request

```json
{
  "template": "promo-produk@1",
  "variables": {
    "judul": "Es Kopi Gula Aren",
    "harga": "Rp18.000",
    "harga_coret": "Rp22.000"
  },
  "idempotency_key": "order-2026-10-20-001"
}
```

## Code samples

### cURL

```bash
curl --request POST \
  --url https://app.mosio.me/v1/videos \
  --header 'Content-Type: application/json' \
  --header 'x-api-key: <x-api-key>' \
  --data '
{
  "template": "promo-produk@1",
  "variables": {
    "judul": "Es Kopi Gula Aren",
    "harga": "Rp18.000",
    "harga_coret": "Rp22.000"
  },
  "idempotency_key": "order-2026-10-20-001"
}
'
```

### TypeScript

```typescript
const url = 'https://app.mosio.me/v1/videos';
const options = {
  method: 'POST',
  headers: {'Content-Type': 'application/json', 'x-api-key': '<x-api-key>'},
  body: JSON.stringify({
    template: 'promo-produk@1',
    variables: {judul: 'Es Kopi Gula Aren', harga: 'Rp18.000', harga_coret: 'Rp22.000'},
    idempotency_key: 'order-2026-10-20-001'
  })
};

fetch(url, options)
  .then(res => res.json())
  .then(json => console.log(json))
  .catch(err => console.error(err));
```

### Python

```python
import requests

url = "https://app.mosio.me/v1/videos"

payload = {
    "template": "promo-produk@1",
    "variables": {
        "judul": "Es Kopi Gula Aren",
        "harga": "Rp18.000",
        "harga_coret": "Rp22.000"
    },
    "idempotency_key": "order-2026-10-20-001"
}
headers = {
    "Content-Type": "application/json",
    "x-api-key": "<x-api-key>"
}

response = requests.post(url, json=payload, headers=headers)

print(response.text)
```

## Responses

### 200

A video with this `idempotency_key` and the same body already exists; it is returned unchanged.

#### Example

```json
{
  "id": "vid_6f1c2b9e-0c1a-4b7e-9d0e-2f3a4b5c6d7e",
  "status": "queued",
  "template": "string",
  "created_at": "2019-08-24T14:15:22Z",
  "finished_at": "2019-08-24T14:15:22Z",
  "duration_seconds": 0,
  "output_url": "string",
  "error": {
    "code": "string",
    "message": "string"
  }
}
```

- `id` (`createVideo.response.200.id`; string, required)
  - example `"vid_6f1c2b9e-0c1a-4b7e-9d0e-2f3a4b5c6d7e"`
- `status` (`createVideo.response.200.status`; string, required)
  - one of `"queued"`, `"rendering"`, `"done"`, `"failed"`, `"canceled"`
- `template` (`createVideo.response.200.template`; string, required) — The template id, `composition` for a rendered composition, or `studio` for a Studio project.
- `created_at` (`createVideo.response.200.created_at`; string, required)
  - format `date-time`
- `finished_at` (`createVideo.response.200.finished_at`; string | null, required)
  - format `date-time`
- `duration_seconds` (`createVideo.response.200.duration_seconds`; number | null, required) — Length of the finished video.
- `output_url` (`createVideo.response.200.output_url`; string | null, required) — Short-lived link to the MP4 once `status` is `done`.
- `error` (`createVideo.response.200.error`; object | null, required) — Why the render failed, when `status` is `failed`. Failed videos spend no credits.
  - `code` (`createVideo.response.200.error.code`; string, optional)
  - `message` (`createVideo.response.200.error.message`; string, optional)

### 201

The video was created and queued.

#### Example

```json
{
  "id": "vid_6f1c2b9e-0c1a-4b7e-9d0e-2f3a4b5c6d7e",
  "status": "queued",
  "template": "string",
  "created_at": "2019-08-24T14:15:22Z",
  "finished_at": "2019-08-24T14:15:22Z",
  "duration_seconds": 0,
  "output_url": "string",
  "error": {
    "code": "string",
    "message": "string"
  }
}
```

- `id` (`createVideo.response.201.id`; string, required)
  - example `"vid_6f1c2b9e-0c1a-4b7e-9d0e-2f3a4b5c6d7e"`
- `status` (`createVideo.response.201.status`; string, required)
  - one of `"queued"`, `"rendering"`, `"done"`, `"failed"`, `"canceled"`
- `template` (`createVideo.response.201.template`; string, required) — The template id, `composition` for a rendered composition, or `studio` for a Studio project.
- `created_at` (`createVideo.response.201.created_at`; string, required)
  - format `date-time`
- `finished_at` (`createVideo.response.201.finished_at`; string | null, required)
  - format `date-time`
- `duration_seconds` (`createVideo.response.201.duration_seconds`; number | null, required) — Length of the finished video.
- `output_url` (`createVideo.response.201.output_url`; string | null, required) — Short-lived link to the MP4 once `status` is `done`.
- `error` (`createVideo.response.201.error`; object | null, required) — Why the render failed, when `status` is `failed`. Failed videos spend no credits.
  - `code` (`createVideo.response.201.error.code`; string, optional)
  - `message` (`createVideo.response.201.error.message`; string, optional)

### 400

The request body is not valid JSON or does not match the schema.

#### Example

```json
{
  "error": "unauthorized",
  "message": "string",
  "details": null
}
```

- `error` (`createVideo.response.400.error`; string, required) — A stable error code.
  - one of `"unauthorized"`, `"invalid_api_key"`, `"insufficient_scope"`, `"forbidden"`, `"invalid_input"`, `"unknown_template"`, `"invalid_variables"`, `"invalid_composition"`, `"idempotency_conflict"`, `"quota_exceeded"`, `"insufficient_credits"`, `"too_many_active_renders"`, `"duration_too_long"`, `"not_found"`, `"internal_error"`
- `message` (`createVideo.response.400.message`; string, required) — A message for people, in Indonesian.
- `details` (`createVideo.response.400.details`; unknown, optional) — More about the error, such as the fields to fix.

### 401

The API key is missing or not valid.

#### Example

```json
{
  "error": "unauthorized",
  "message": "Kirim API key di header x-api-key."
}
```

- `error` (`createVideo.response.401.error`; string, required) — A stable error code.
  - one of `"unauthorized"`, `"invalid_api_key"`, `"insufficient_scope"`, `"forbidden"`, `"invalid_input"`, `"unknown_template"`, `"invalid_variables"`, `"invalid_composition"`, `"idempotency_conflict"`, `"quota_exceeded"`, `"insufficient_credits"`, `"too_many_active_renders"`, `"duration_too_long"`, `"not_found"`, `"internal_error"`
- `message` (`createVideo.response.401.message`; string, required) — A message for people, in Indonesian.
- `details` (`createVideo.response.401.details`; unknown, optional) — More about the error, such as the fields to fix.

### 402

The workspace does not have enough credits for this video.

#### Example

```json
{
  "error": "unauthorized",
  "message": "string",
  "details": null
}
```

- `error` (`createVideo.response.402.error`; string, required) — A stable error code.
  - one of `"unauthorized"`, `"invalid_api_key"`, `"insufficient_scope"`, `"forbidden"`, `"invalid_input"`, `"unknown_template"`, `"invalid_variables"`, `"invalid_composition"`, `"idempotency_conflict"`, `"quota_exceeded"`, `"insufficient_credits"`, `"too_many_active_renders"`, `"duration_too_long"`, `"not_found"`, `"internal_error"`
- `message` (`createVideo.response.402.message`; string, required) — A message for people, in Indonesian.
- `details` (`createVideo.response.402.details`; unknown, optional) — More about the error, such as the fields to fix.

### 404

No such template or video in this workspace.

#### Example

```json
{
  "error": "unauthorized",
  "message": "string",
  "details": null
}
```

- `error` (`createVideo.response.404.error`; string, required) — A stable error code.
  - one of `"unauthorized"`, `"invalid_api_key"`, `"insufficient_scope"`, `"forbidden"`, `"invalid_input"`, `"unknown_template"`, `"invalid_variables"`, `"invalid_composition"`, `"idempotency_conflict"`, `"quota_exceeded"`, `"insufficient_credits"`, `"too_many_active_renders"`, `"duration_too_long"`, `"not_found"`, `"internal_error"`
- `message` (`createVideo.response.404.message`; string, required) — A message for people, in Indonesian.
- `details` (`createVideo.response.404.details`; unknown, optional) — More about the error, such as the fields to fix.

### 409

This `idempotency_key` was used before with a different body.

#### Example

```json
{
  "error": "unauthorized",
  "message": "string",
  "details": null
}
```

- `error` (`createVideo.response.409.error`; string, required) — A stable error code.
  - one of `"unauthorized"`, `"invalid_api_key"`, `"insufficient_scope"`, `"forbidden"`, `"invalid_input"`, `"unknown_template"`, `"invalid_variables"`, `"invalid_composition"`, `"idempotency_conflict"`, `"quota_exceeded"`, `"insufficient_credits"`, `"too_many_active_renders"`, `"duration_too_long"`, `"not_found"`, `"internal_error"`
- `message` (`createVideo.response.409.message`; string, required) — A message for people, in Indonesian.
- `details` (`createVideo.response.409.details`; unknown, optional) — More about the error, such as the fields to fix.

### 422

Some variables are missing or too long, or the output size is not supported. `details` names the variables.

#### Example

```json
{
  "error": "invalid_variables",
  "message": "Variabel template tidak valid.",
  "details": {
    "harga": [
      {
        "Too big": "expected string to have <=24 characters"
      }
    ]
  }
}
```

- `error` (`createVideo.response.422.error`; string, required) — A stable error code.
  - one of `"unauthorized"`, `"invalid_api_key"`, `"insufficient_scope"`, `"forbidden"`, `"invalid_input"`, `"unknown_template"`, `"invalid_variables"`, `"invalid_composition"`, `"idempotency_conflict"`, `"quota_exceeded"`, `"insufficient_credits"`, `"too_many_active_renders"`, `"duration_too_long"`, `"not_found"`, `"internal_error"`
- `message` (`createVideo.response.422.message`; string, required) — A message for people, in Indonesian.
- `details` (`createVideo.response.422.details`; unknown, optional) — More about the error, such as the fields to fix.

### 429

The workspace already has the maximum number of videos queued or rendering (2). Wait for one to finish.

#### Example

```json
{
  "error": "unauthorized",
  "message": "string",
  "details": null
}
```

- `error` (`createVideo.response.429.error`; string, required) — A stable error code.
  - one of `"unauthorized"`, `"invalid_api_key"`, `"insufficient_scope"`, `"forbidden"`, `"invalid_input"`, `"unknown_template"`, `"invalid_variables"`, `"invalid_composition"`, `"idempotency_conflict"`, `"quota_exceeded"`, `"insufficient_credits"`, `"too_many_active_renders"`, `"duration_too_long"`, `"not_found"`, `"internal_error"`
- `message` (`createVideo.response.429.message`; string, required) — A message for people, in Indonesian.
- `details` (`createVideo.response.429.details`; unknown, optional) — More about the error, such as the fields to fix.


Source: https://docs.mosio.me/api/videos/create/index.md
