Videos
Videos are file-based widgets that display and play back video content on a Canvus canvas. Use videos for presentations, recorded demos, tutorials, or any motion content that needs to live alongside other canvas widgets.
Video files are uploaded via multipart form data and can be downloaded through a dedicated endpoint. The API provides full playback control including play, pause, stop, and seek, as well as mute/unmute. The server also exposes the video duration once a Canvus desktop client has decoded the file.
Authentication
All endpoints require authentication via the Private-Token header or a CanvusSession cookie, unless the canvas has link sharing enabled (View or Edit permission). Write operations (POST, PATCH, DELETE) require edit access. View-only users receive 403 Forbidden on any write attempt.
Common Widget Properties
Every video shares these properties with all other canvas widget types.
id-- string (uuid, read-only). Unique identifier assigned by the server on creation.widget_type-- string (read-only). Always"Video"for this endpoint.location-- object (patchable). Position relative to the parent widget, withxandyfloat fields.size-- object (patchable). Dimensions in canvas units, withwidthandheightfloat fields. Aspect ratio is enforced -- the requested size is treated as a bounding box and the video fits within it using longest-edge scaling. The response contains the actual applied dimensions.depth-- float (patchable). Z-order among sibling widgets. Higher values render on top. Must be >= 1.0 -- the server returns 400 Bad Request if you set depth below 1.0.scale-- float (patchable). Uniform scale factor applied to the widget. Default:1.pinned-- boolean (patchable). Whentrue, the widget cannot be moved or resized through touch interaction. API changes still apply. Default:false.state-- string (read-only). Widget state, typically"normal".parent_id-- string (uuid, patchable). ID of the parent widget. Changing this reparents the widget. Note thatlocationcoordinates are always relative to the parent -- changingparent_idwithout updatinglocationwill reinterpret the same x/y values in the new parent's coordinate space.auto_raise-- boolean (patchable, PATCH only). Whentrue, the server sets the widget's depth to the highest sibling depth + 1.0, bringing it to the front. Not persisted -- this is a one-shot parameter. Default:false.
Video-Specific Properties
title-- string (patchable). Display title for the video widget. Default:"".original_filename-- string (patchable). The original filename of the uploaded video file. Default:"".hash-- string (read-only). Server-generated content hash of the video file. Empty string immediately after creation while the file is being processed.playback_state-- string (patchable). Current playback state. One of:"playing","paused","stopped". Default:"stopped". Case-insensitive on input but returned as uppercase ("STOPPED","PLAYING","PAUSED") in responses.playback_position-- float (patchable). Current playback position in seconds. Default:0. When you setplayback_stateto"paused"without providingplayback_position, the server automatically computes and preserves the current position based on elapsed time since playback started.muted-- boolean (patchable). Whentrue, video audio is silenced. Default:false.duration-- float (read-only). Total duration of the video in seconds. This value is0.0until a Canvus desktop client decodes the video file and reports its duration. Do not set this via the API.
Streaming
All GET endpoints support real-time updates via the ?subscribe query parameter. When present, the server holds the connection open and sends newline-delimited JSON whenever the resource changes. The server sends periodic keepalive pings to maintain the connection.
List Videos
Retrieves all video widgets on a canvas.
GET /api/v1/canvases/:canvas_id/videos
Path parameters:
canvas_id(uuid, required) -- ID of the canvas
Query parameters:
subscribe(boolean, optional) -- Enable streaming updates
Example request:
curl -H "Private-Token: YOUR_TOKEN" \
https://canvus.example.com/api/v1/canvases/a8b5ed9e-6e57-41c8-96cb-be4aec4bfd03/videos
Example response:
[
{
"depth": 4.0,
"duration": 127.5,
"hash": "863b0937ee95",
"id": "105a8486-ac84-49f9-a487-763279a6f658",
"location": {
"x": 3737.21,
"y": 2311.91
},
"muted": false,
"original_filename": "product-demo.mp4",
"parent_id": "34f8909a-6736-49f9-9027-7f7cab5c1acf",
"pinned": false,
"playback_position": 0,
"playback_state": "STOPPED",
"scale": 1,
"size": {
"height": 720,
"width": 1280
},
"state": "normal",
"title": "Product Demo",
"widget_type": "Video"
}
]
Get Single Video
Retrieves a single video widget by ID.
GET /api/v1/canvases/:canvas_id/videos/:video_id
Path parameters:
canvas_id(uuid, required) -- ID of the canvasvideo_id(uuid, required) -- ID of the video
Query parameters:
subscribe(boolean, optional) -- Enable streaming updates
Example request:
curl -H "Private-Token: YOUR_TOKEN" \
https://canvus.example.com/api/v1/canvases/a8b5ed9e-6e57-41c8-96cb-be4aec4bfd03/videos/105a8486-ac84-49f9-a487-763279a6f658
Example response:
{
"depth": 4.0,
"duration": 127.5,
"hash": "863b0937ee95",
"id": "105a8486-ac84-49f9-a487-763279a6f658",
"location": {
"x": 3737.21,
"y": 2311.91
},
"muted": false,
"original_filename": "product-demo.mp4",
"parent_id": "34f8909a-6736-49f9-9027-7f7cab5c1acf",
"pinned": false,
"playback_position": 0,
"playback_state": "STOPPED",
"scale": 1,
"size": {
"height": 720,
"width": 1280
},
"state": "normal",
"title": "Product Demo",
"widget_type": "Video"
}
Create Video
Creates a new video widget by uploading a file. The request must be multipart/form-data with a mandatory data part (the video file) and an optional json part (widget metadata).
POST /api/v1/canvases/:canvas_id/videos
Path parameters:
canvas_id(uuid, required) -- ID of the canvas
Multipart form parts:
data(file, required) -- The video file to upload (MP4, WebM, MOV, etc.)json(string, optional) -- JSON string with widget properties
JSON properties (all optional):
title-- stringoriginal_filename-- stringlocation-- object withxandyfloatssize-- object withwidthandheightfloatsdepth-- float (must be >= 1.0)scale-- floatpinned-- booleanparent_id-- string (uuid)playback_state-- string ("stopped","playing", or"paused")playback_position-- float (seconds)muted-- boolean
Example request:
curl -X POST \
-H "Private-Token: YOUR_TOKEN" \
-F 'json={"title": "Product Demo", "muted": true}' \
-F 'data=@product-demo.mp4' \
https://canvus.example.com/api/v1/canvases/a8b5ed9e-6e57-41c8-96cb-be4aec4bfd03/videos
Example response:
{
"depth": 1.0,
"duration": 0.0,
"hash": "",
"id": "370b112b-c6e9-440f-bdad-f6d6cf6a6d9f",
"location": {
"x": 5148.75,
"y": 2807.72
},
"muted": true,
"original_filename": "product-demo.mp4",
"parent_id": "34f8909a-6736-49f9-9027-7f7cab5c1acf",
"pinned": false,
"playback_position": 0,
"playback_state": "STOPPED",
"scale": 1,
"size": {
"height": 720,
"width": 1280
},
"state": "normal",
"title": "Product Demo",
"widget_type": "Video"
}
The hash field is empty immediately after creation and the duration is 0.0. Both are populated once the file is processed (hash) and decoded by a desktop client (duration). Subscribe to the widget or poll with GET to see updated values.
Update Video
Updates one or more properties of an existing video widget. Only the fields included in the request body are changed -- all other properties remain untouched.
PATCH /api/v1/canvases/:canvas_id/videos/:video_id
Path parameters:
canvas_id(uuid, required) -- ID of the canvasvideo_id(uuid, required) -- ID of the video to update
Request body (JSON):
Any combination of patchable properties: title, original_filename, location, size, depth, scale, pinned, parent_id, auto_raise, playback_state, playback_position, muted.
Example request -- start playback:
curl -X PATCH \
-H "Private-Token: YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{"playback_state": "playing"}' \
https://canvus.example.com/api/v1/canvases/a8b5ed9e-6e57-41c8-96cb-be4aec4bfd03/videos/105a8486-ac84-49f9-a487-763279a6f658
Example request -- pause (position preserved automatically):
curl -X PATCH \
-H "Private-Token: YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{"playback_state": "paused"}' \
https://canvus.example.com/api/v1/canvases/a8b5ed9e-6e57-41c8-96cb-be4aec4bfd03/videos/105a8486-ac84-49f9-a487-763279a6f658
When pausing without providing playback_position, the server computes the current position from the seek position plus elapsed time since playback started. There is no need to calculate and send the position yourself.
Example request -- seek to specific position and play:
curl -X PATCH \
-H "Private-Token: YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{"playback_state": "playing", "playback_position": 45.0}' \
https://canvus.example.com/api/v1/canvases/a8b5ed9e-6e57-41c8-96cb-be4aec4bfd03/videos/105a8486-ac84-49f9-a487-763279a6f658
Example request -- mute:
curl -X PATCH \
-H "Private-Token: YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{"muted": true}' \
https://canvus.example.com/api/v1/canvases/a8b5ed9e-6e57-41c8-96cb-be4aec4bfd03/videos/105a8486-ac84-49f9-a487-763279a6f658
Example request -- resize (aspect ratio enforced):
curl -X PATCH \
-H "Private-Token: YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{"size": {"width": 640, "height": 640}}' \
https://canvus.example.com/api/v1/canvases/a8b5ed9e-6e57-41c8-96cb-be4aec4bfd03/videos/105a8486-ac84-49f9-a487-763279a6f658
The server enforces the video's native aspect ratio. The requested size is treated as a bounding box. If you request 640x640 for a 16:9 video, the response will contain {"width": 640, "height": 360}. Always read the response body to get the actual applied size.
Example response:
{
"depth": 4.0,
"duration": 127.5,
"hash": "863b0937ee95",
"id": "105a8486-ac84-49f9-a487-763279a6f658",
"location": {
"x": 3737.21,
"y": 2311.91
},
"muted": true,
"original_filename": "product-demo.mp4",
"parent_id": "34f8909a-6736-49f9-9027-7f7cab5c1acf",
"pinned": false,
"playback_position": 45.0,
"playback_state": "PLAYING",
"scale": 1,
"size": {
"height": 360,
"width": 640
},
"state": "normal",
"title": "Product Demo",
"widget_type": "Video"
}
Validation errors:
- Setting
depthbelow 1.0 returns 400 Bad Request:Depth must be >= 1.0, got <value> - Setting
sizewith zero or negative dimensions is rejected
Download Video
Downloads the original video file as a binary attachment.
GET /api/v1/canvases/:canvas_id/videos/:video_id/download
Path parameters:
canvas_id(uuid, required) -- ID of the canvasvideo_id(uuid, required) -- ID of the video to download
Example request:
curl -H "Private-Token: YOUR_TOKEN" \
-o downloaded-video.mp4 \
https://canvus.example.com/api/v1/canvases/a8b5ed9e-6e57-41c8-96cb-be4aec4bfd03/videos/105a8486-ac84-49f9-a487-763279a6f658/download
The response includes a content-disposition: attachment header with the original filename. Immutable assets include cache-control: private, max-age=157680000, immutable for aggressive client-side caching.
Delete Video
Permanently removes a video widget and its associated file from the canvas.
DELETE /api/v1/canvases/:canvas_id/videos/:video_id
Path parameters:
canvas_id(uuid, required) -- ID of the canvasvideo_id(uuid, required) -- ID of the video to delete
Example request:
curl -X DELETE \
-H "Private-Token: YOUR_TOKEN" \
https://canvus.example.com/api/v1/canvases/a8b5ed9e-6e57-41c8-96cb-be4aec4bfd03/videos/105a8486-ac84-49f9-a487-763279a6f658
A successful deletion returns an empty response with status 200 OK.
Error Responses
All endpoints return errors as JSON with a msg field.
- 400 Bad Request -- Invalid input (e.g. depth below 1.0, missing file in upload, zero-size dimensions)
- 401 Unauthorized -- Missing or invalid authentication token
- 403 Forbidden -- View-only user attempted a write operation
- 404 Not Found -- Canvas or video does not exist
Example error response:
{"msg": "Depth must be >= 1.0, got 0.0"}