Skip to content

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, with x and y float fields.
  • size -- object (patchable). Dimensions in canvas units, with width and height float 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). When true, 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 that location coordinates are always relative to the parent -- changing parent_id without updating location will reinterpret the same x/y values in the new parent's coordinate space.
  • auto_raise -- boolean (patchable, PATCH only). When true, 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 set playback_state to "paused" without providing playback_position, the server automatically computes and preserves the current position based on elapsed time since playback started.
  • muted -- boolean (patchable). When true, video audio is silenced. Default: false.
  • duration -- float (read-only). Total duration of the video in seconds. This value is 0.0 until 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 canvas
  • video_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 -- string
  • original_filename -- string
  • location -- object with x and y floats
  • size -- object with width and height floats
  • depth -- float (must be >= 1.0)
  • scale -- float
  • pinned -- boolean
  • parent_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 canvas
  • video_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 depth below 1.0 returns 400 Bad Request: Depth must be >= 1.0, got <value>
  • Setting size with 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 canvas
  • video_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 canvas
  • video_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"}