Videos
The video object
| Field | Type | Description |
|---|---|---|
| id | string | Unique identifier for the object. |
| project_id | string | |
| folder_id | string | |
| version | number | |
| title | string | |
| description | string | |
| status | enum | Available values: pending, uploading, pre-processing, processing, aborted, done, error |
| progress | number | Video processing progress |
| duration | number | Video duration in seconds |
| assets | []Asset | |
| has_audio | bool | Whether the video has any audio. |
| audio_tracks | []AudioTrack | Downloadable audio tracks (id, language, label, file_size, filetype, original_name, download_link). Omitted when the video has no audio tracks. |
| chapters | Chapters | |
| privacy_type | enum | Available values: anywhere, nowhere, custom |
| privacy_domains | []string | Whitelist domains |
| poster | Poster | |
| additional_materials | []AdditionalMaterial | |
| additional_materials_enabled | bool | |
| annotation_text_enabled | bool | |
| annotation_video_enabled | bool | |
| created_at | datetime | |
| updated_at | datetime | |
| play_link | string | Link to player |
| embed_link | string | Embed player link |
| subtitles | Subtitles | |
| subtitles_enabled | boolean | Show/Hide subtitles in player |
| hls_link | string | Link to HLS manifest |
Concat video
https://api.kinescope.io/v1/videos/:video_id/concatJoins the source video with other videos: prepend goes before it, append after it. By default the result is a new video and the source stays unchanged. With replace: true the result is written back to the source the same way as in Cut video: the ID, player, statistics and links stay, the previous version plays until processing finishes, and another replace while the previous one is running returns 409322.
Without replace the new video gets the source's title and description and lands in the same folder; the project or folder cannot be chosen. The body has no title, description, project_id or folder_id: these and any other unknown fields are silently ignored.
| Parameter | Type | Description |
|---|---|---|
prepend | array of object | Videos placed before the source. Each item has video_id. |
append | array of object | Videos placed after the source. Each item has video_id. |
replace | boolean | Write the result back to the source video instead of creating a new one. Default: false. |
| Code | HTTP | When |
|---|---|---|
400276 | — | More than 10 videos to join. |
400277 | — | A video from prepend or append is not in this workspace. The error names the field. |
409322 | 409 | With replace: true: the previous edit of this video has not finished. Retry after it completes. |
{
"prepend": [
{
"video_id": "<UUID>"
}
],
"append": [
{
"video_id": "<UUID>"
}
],
"replace": false
}Copy video
https://api.kinescope.io/v1/videos/:video_id/copyCreates a full copy of the video, optionally moving the copy into a different project or folder.
{
"parent_id": "<UUID>"
}Cut video
https://api.kinescope.io/v1/videos/:video_id/cutTrims the video, crops the frame, or both. By default the result is a new video and the source stays unchanged. With replace: true the result is written back to the source: its ID, player, statistics and links stay the same.
The request returns at once, and processing continues in the background. Both responses below are trimmed: the full object is the same as in Get video.
Without replace the response is a new video with a new id and status: "pending":
- It is created in the same project or folder as the source.
project_idplaces it in another project of the workspace, andfolder_idin a folder of that project.folder_idwithoutproject_idis ignored, and a project outside the workspace returnsACCESS_DENIED. - Without
titlethe new video is named<source title> / tizer. Withoutdescriptionit gets the source description. - Privacy and other settings are taken from the source's project, not from the source video. Chapters, subtitles and annotations are not copied.
With replace: true the source is processed again in place:
- Pass
trim,cropor both — at least one is required.title,description,project_idandfolder_idare rejected with400400, oneinvalid_paramsentry per field, and the video keeps its own. An empty string ornullcounts as not passed. - The response is the same video with its previous
status(usuallydone) andduration. Nothing in it shows that an edit is in progress. - Until processing finishes, the player — embedded or opened by link — plays the previous version. Then it switches to the new one, and the cache is reset.
- When every quality is ready, the
media.update.statuswebhook arrives withstatus: "done". Nothing is sent when the edit starts. - Chapters, subtitles and annotations are kept as they are, and their timings do not shift. After trimming they can drift from the video or point past its new end: update them with Update chapters and the subtitle and annotation methods.
- Another
replaceof the same video cannot start while the previous one is running: the request returns409322.
When a replace fails. If the edit itself fails — for example, on an invalid crop rectangle — no webhook arrives, the video stays as it was with status: "done", and the API does not report the error. If re-encoding fails, the media.update.status webhook arrives with status: "error", but the video status stays done and the replaced version plays in a single quality.
| Parameter | Type | Description |
|---|---|---|
replace | boolean | Write the result back to the source video instead of creating a new one. Default: false. |
trim | array of object | Segments to keep; they are joined in the order given. start — where the segment begins, length — how long it lasts, both in seconds, fractions allowed. |
crop | object | Frame area to keep. quality is required: the quality whose resolution the coordinates refer to, for example 360p; without it the request returns 400400. w, h — width and height, greater than 0. x, y — top-left corner, 0 or more. w, h, x and y are checked only during processing: with replace: true an invalid value fails the edit silently. |
project_id | uuid | Without replace only. Project for the new video. Default: the source's project or folder. |
folder_id | uuid | Without replace only. Folder inside project_id; ignored without it. |
title | string | Without replace only. Default: <source title> / tizer. |
description | string | Without replace only. Default: the source description. |
| Code | HTTP | When |
|---|---|---|
400400 | 400 | crop without quality. With replace: true — any of title, description, project_id, folder_id, each with reason: "excluded_with". |
100103 | 403 | Without replace: project_id is not a project of this workspace (ACCESS_DENIED). |
404404 | 404 | The video is not found. |
409322 | 409 | With replace: true: the previous edit of this video has not finished. Retry after it completes. |
{
"replace": true,
"trim": [
{
"start": 10,
"length": 120
}
]
}{
"data": {
"id": "<UUID>",
"project_id": "<UUID>",
"folder_id": null,
"title": "Product demo",
"status": "done",
"duration": 366.5
}
}{
"data": {
"id": "<UUID>",
"project_id": "<UUID>",
"folder_id": null,
"title": "Product demo / tizer",
"status": "pending"
}
}{
"error": {
"code": 400400,
"message": "request parameters didn't validate",
"invalid_params": [
{
"name": "title",
"reason": "excluded_with",
"message": "Only one parameter can be set: title or Replace"
}
]
}
}Videos list
https://api.kinescope.io/v1/videosReturns a list of videos. Results are paginated and can be filtered via query parameters.
{
"meta": {
"pagination": {
"page": 1,
"per_page": 1,
"total": 10
},
"order": {
"created_at": "desc",
"title": "asc"
}
},
"data": [
{
"id": "vKSxQyZScoQfP29oothREi",
"project_id": "mNFQz2wYTBhLkPBCo9oS8m",
"folder_id": null,
"version": 0,
"title": "sample_video",
"description": "",
"status": "uploading",
"progress": 100,
"duration": 0,
"assets": [],
"chapters": {
"items": [],
"enabled": false,
"show_on_load": false
},
"privacy_type": "anywhere",
"privacy_domains": [],
"poster": null,
"additional_materials": null,
"additional_materials_enabled": false,
"play_link": "https://kinescope.io/199511994",
"embed_link": "https://kinescope.io/embed/199511994",
"created_at": "2020-08-31T09:03:00.106436Z",
"updated_at": null,
"subtitles": [],
"subtitles_enabled": false,
"hls_link": "https://kinescope.io/199511994/master.m3u8"
}
]
}Get video
https://api.kinescope.io/v1/videos/:video_idRetrieves the details of a single video by its unique ID.
{
"data": {
"id": "<UUID>",
"project_id": "<UUID>",
"folder_id": null,
"player_id": "<UUID>",
"version": 1,
"title": " Encrypted, saved (2024.09.08 13:35)",
"subtitle": "",
"description": "",
"status": "done",
"progress": 0,
"duration": 66.00001,
"assets": [
{
"id": "<UUID>",
"video_id": "<UUID>",
"original_name": "original.ts",
"file_size": 4862244,
"md5": "a05d6c66884dc7f8c5136b6c9d95fef7",
"filetype": "ts",
"quality": "original",
"resolution": "1920x1080",
"created_at": "2024-09-08T10:35:17.087638Z",
"url": "https://kinescopecdn.net/<UUID>/videos/<UUID>/assets/<UUID>/original.mp4?expires=<EXPIRES>&sign=<SIGNATURE>",
"download_link": "https://s3.kinescope.io/<UUID>/videos/<UUID>/mp4/original.ts?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=<CREDENTIAL>"
},
{
"id": "<UUID>",
"video_id": "<UUID>",
"original_name": "1080p.mp4",
"file_size": 4292636,
"filetype": "mp4",
"quality": "1080p",
"resolution": "1920x1080",
"created_at": "2024-09-08T10:35:30.549646Z",
"download_link": ""
},
{
"id": "<UUID>",
"video_id": "<UUID>",
"original_name": "720p.mp4",
"file_size": 3217594,
"filetype": "mp4",
"quality": "720p",
"resolution": "1280x720",
"created_at": "2024-09-08T10:35:30.549646Z",
"download_link": ""
},
{
"id": "<UUID>",
"video_id": "<UUID>",
"original_name": "480p.mp4",
"file_size": 2031032,
"filetype": "mp4",
"quality": "480p",
"resolution": "852x480",
"created_at": "2024-09-08T10:35:30.549646Z",
"download_link": ""
},
{
"id": "<UUID>",
"video_id": "<UUID>",
"original_name": "360p.mp4",
"file_size": 1451443,
"filetype": "mp4",
"quality": "360p",
"resolution": "640x360",
"created_at": "2024-09-08T10:35:30.549646Z",
"download_link": ""
}
],
"has_audio": true,
"audio_tracks": [
{
"id": "<UUID>",
"language": "en",
"label": "English",
"file_size": 4292636,
"filetype": "mp4",
"original_name": "audio_en.mp4",
"download_link": "https://s3.kinescope.io/<UUID>/videos/<UUID>/audio/<UUID>.mp4?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=<CREDENTIAL>"
},
{
"id": "<UUID>",
"language": "ru",
"label": "Русский",
"file_size": 4185220,
"filetype": "mp4",
"original_name": "audio_ru.mp4",
"download_link": "https://s3.kinescope.io/<UUID>/videos/<UUID>/audio/<UUID>.mp4?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=<CREDENTIAL>"
}
],
"chapters": {
"items": [],
"enabled": false
},
"privacy_type": "anywhere",
"privacy_domains": [],
"privacy_email_domains": [],
"privacy_share": {},
"tags": [],
"poster": {
"id": "<UUID>",
"type": "image",
"media_id": "<UUID>",
"status": "done",
"active": true,
"original": "https://kinescopecdn.net/<UUID>/posters/<UUID>/<UUID>.jpg",
"md": "https://kinescopecdn.net/<UUID>/posters/<UUID>/md/<UUID>.jpg",
"sm": "https://kinescopecdn.net/<UUID>/posters/<UUID>/sm/<UUID>.jpg",
"xs": "https://kinescopecdn.net/<UUID>/posters/<UUID>/xs/<UUID>.jpg",
"from_time": 0,
"to_time": 0
},
"additional_materials": [],
"additional_materials_enabled": false,
"annotation_text_enabled": false,
"annotation_video_enabled": false,
"play_link": "https://kinescope.io/vN1xLXadcCk94aCuZHR5i2",
"embed_link": "https://kinescope.io/embed/vN1xLXadcCk94aCuZHR5i2",
"created_at": "2024-09-08T10:35:11.674333Z",
"updated_at": "2026-03-05T14:04:37.150678Z",
"subtitles": [],
"subtitles_enabled": false,
"hls_link": "https://kinescope.io/vN1xLXadcCk94aCuZHR5i2/master.m3u8",
"chat_preview": false,
"chat_inside_player": "",
"chat_expand_on_load": false,
"meta": {
"event_id": "<UUID>",
"stream_id": "<UUID>"
}
}
}{
"error": {
"code": 400404,
"message": "not found"
}
}Move video to another project and folder
https://api.kinescope.io/v1/videos/:video_id/moveMoves the video into a different project and/or folder. The video ID stays the same.
{
"project_id": "3kAyiWTAZQKKZxg1GfjKvS",
"folder_id": "2MxB8wx35jHQjhCPczBamu"
}Update chapters
https://api.kinescope.io/v1/videos/:video_id/chaptersReplaces the chapter markers of the video with the provided list. Pass an empty array to clear all chapters.
{
"items": [
{
"time": 10000,
"title": "Chapter at 10sec"
}
],
"enabled": true
}{
"data": {
"items": [
{
"time": 10000,
"title": "Chapter at 10sec"
}
],
"enabled": true
}
}Update video
https://api.kinescope.io/v1/videos/:video_idUpdates the specified video. Only fields provided in the request body are changed; others are left unchanged.
This request update only passed fields.
Fields which can update: title, description, privacy_type, privacy_domains, additional_materials_enabled
{
"title": "New title",
"description": "New description",
"privacy_type": "custom",
"privacy_domains": [
"example.com"
],
"additional_materials_enabled": false,
"tags": [
"tag 1",
"tag 2",
"tag 3"
]
}{
"data": {
"id": "iuFPEzdC2Jtf8aCcvMJQ8h",
"workspace_id": "s8vL2Zr3CxCj1A8HmxoGhp",
"project_id": "hfHfAgq4g2nxxtg8DQ3Twi",
"folder_id": null,
"storage_id": "mucjva43n3sLsw5YRwsZYS",
"short_id": 199511990,
"version": 0,
"title": "New title",
"description": "New description",
"status": "done",
"progress": 0,
"duration": 179.305,
"assets": [
{
"id": "i7r58w3Lt3bPoPyX27tprT",
"video_id": "iuFPEzdC2Jtf8aCcvMJQ8h",
"original_name": "original.mp4",
"file_size": 5603249,
"filetype": "QuickTime / MOV",
"quality": "original",
"resolution": "320x240",
"created_at": "2020-08-30T16:10:08.091561Z",
"updated_at": null,
"deleted_at": null
},
{
"id": "kdwSHkB2e1AH8veDLuCnUP",
"video_id": "iuFPEzdC2Jtf8aCcvMJQ8h",
"original_name": "360p.mp4",
"file_size": 4196287,
"filetype": "QuickTime / MOV",
"quality": "360p",
"resolution": "320x240",
"created_at": "2020-08-30T16:10:14.087606Z",
"updated_at": null,
"deleted_at": null
}
],
"chapters": {
"items": [],
"enabled": false,
"show_on_load": false
},
"privacy_type": "custom",
"privacy_domains": [
"example.com"
],
"poster": {
"id": "eHqAqKuMi4JTsAoMvf5GRs",
"video_id": "iuFPEzdC2Jtf8aCcvMJQ8h",
"storage_id": "mucjva43n3sLsw5YRwsZYS"
},
"additional_materials": [],
"additional_materials_enabled": false,
"created_at": "2020-08-30T16:10:05.702154Z",
"updated_at": null,
"deleted_at": null
}
}Delete video
https://api.kinescope.io/v1/videos/:video_idPermanently deletes the video. This action cannot be undone.
{
"data": {
"success": true
}
}Videos
Create upload session
https://uploader.kinescope.io/v2/initCreates an upload session and returns the address the file is sent to. Use it when the file is large, the network is unreliable, or the upload happens in a browser you do not want to hand the API token to: the address itself authorises the upload.
The returned endpoint is geo-routed — https://eu-ams-uploader-1.kinescope.io/v2/upload/… — so send the file exactly there, do not rebuild the URL from the base host.
| Parameter | Type | Description |
|---|---|---|
type | enum | Required. video, attachment or replace. |
parent_id | uuid | Required for type: video. Project or folder the video lands in. Get it from GET /v1/projects or GET /v1/projects/:project_id/folders. |
title | string | Required for type: video. Video title. |
video_id | uuid | Required for type: attachment. The video the material is attached to. |
filesize | integer | Required for tus. File size in bytes. Drives the progress indicator. |
filename | string | File name. Informational. |
subtitle | string | Video subtitle. |
description | string | Video description. |
client_ip | string | End-client IP address. Picks the closest upload server. |
preview | object | Creates a preview clip available at https://kinescope.io/{video_id}/preview. Fields: start and length in seconds, quality — 360p, 480p, 720p or 1080p. |
The parameter set for type: replace is not described in the guide and requires confirmation.
{
"type": "video",
"parent_id": "e51e55a1-7615-493e-9055-10ac9cc44ccd",
"title": "Product demo",
"filename": "demo.mp4",
"filesize": 48622440
}{
"data": {
"id": "7127f2d7-0e96-40d0-9a03-2e987c096466",
"endpoint": "https://eu-ams-uploader-1.kinescope.io/v2/upload/0966958f-638b-4aab-bf4a-7f9860a57a93"
}
}Step 2: send the file to the returned address
The address from data.endpoint accepts both a plain POST with the file in the body and the tus protocol. The access token is not sent on this step — the address itself is the permission.
curl -X POST \
'https://eu-ams-uploader-1.kinescope.io/v2/upload/0966958f-638b-4aab-bf4a-7f9860a57a93' \
--data-binary '@demo.mp4'For resumable uploads use a tus client library: it sends HEAD to read the current Upload-Offset and continues with PATCH from that byte, receiving 204 No Content with the new offset after every chunk. A plain POST has no resume — after a drop the upload starts from zero.
How long the returned address stays valid is not documented and requires confirmation. The threshold above which tus becomes mandatory is not documented either.
Upload video
https://uploader.kinescope.io/v2/videoUploads a video in a single request. Metadata goes in headers, the file goes in the body — or, with the X-Video-URL header, the body stays empty and Kinescope downloads the file itself.
Use it for small files on a stable connection and for imports from your own storage. For large files use POST /v2/init and tus: this endpoint has no resume, and a dropped connection means starting over.
| Parameter | Type | Description |
|---|---|---|
X-Parent-ID | uuid | Required. Project or folder the video lands in. |
X-Video-Title | string | Required. Video title. |
X-Video-URL | string | Required for URL upload. Direct link to the file, or a YouTube address of the form https://www.youtube.com/watch?v=… or https://youtu.be/…. With this header the request body stays empty. |
X-Video-Description | string | Video description. |
X-File-Name | string | File name. |
X-Replace-Video-ID | uuid | Video to replace — the file goes into it instead of a new video, see below the table. |
X-Video-Trim | json | Trim on upload, values in seconds: {"start": 1, "length": 2}. Without length the video is trimmed from start to the end. |
Content-Type | string | Type of the data in the body, for example video/mp4. |
Replacing a video. With X-Replace-Video-ID the file goes into the existing video: its ID, links, player and statistics stay the same, and chapters, subtitles and annotations are not touched. While the new file is processed, the status stays done and the previous version plays. The title is always overwritten by X-Video-Title, which every upload requires; the description is overwritten only by a non-empty X-Video-Description. The file name and the author (author_id, author_created_at) are updated, and the edit history in the built-in editor is reset. While a replacement is running, it cannot be repeated and a parallel one cannot be started: such a request returns an error.
X-Poster-URL appears in the previous version of this page but not in the developer guide: its support requires confirmation.
The body format is described as multipart in the previous version of this page, while the guide passes the file as a raw body. Maximum file size for this endpoint is not published. Both require confirmation.
{
"data": {
"id": "8d1c4b7e-2f39-4a06-b5c8-71e0a9d3f462",
"project_id": "3f2a9c14-7b6d-4e58-9a21-0c5e8d47b193",
"title": "Product demo",
"status": "uploading",
"progress": 0,
"play_link": "https://kinescope.io/bK3mQx7ZtNvRs2WdYpLc9F"
}
}After the upload
A finished upload does not mean a ready video. Poll GET /v1/videos/{video_id} by the identifier returned at the first step, no more than once every 5 seconds, or subscribe to the media.update.status webhook. Terminal statuses are done, error and aborted; progress is not a readiness signal.
If the file behind X-Video-URL is unreachable, the video ends up in status: error and the reason arrives in the webhook payload.
Repeating a POST after a timeout creates a second video: Idempotency-Key is not supported. Before retrying, look the video up with GET /v1/videos filtered by project and title, or replace the failed attempt through X-Replace-Video-ID.