Control Canvas Content
Quick Start
Requirements
- Get and save an API key from Dot. App;
- Get the serial number of a device you own;
- Make sure the device is powered and online;
- Add the
Canvas APIcontent to the device loop task in Dot. App Content Studio.
What Canvas API is for
Text API is best for quickly sending a text card, and Image API is best for showing one image. Canvas API lets you turn your own data into custom cards, dashboards, or small visual experiments.
windowData uses object elements
windowData is written with React-elements-like objects: each element is an object, type selects the element type, props contains style and content, and props.children can be text, one child element, or an array of child elements.
The outer container usually needs no padding
The device layout handles the basic spacing outside windowData.default. Let the outer element focus on structure, background, and alignment. When you need edge-to-edge rendering, a custom background, or FULL layout overrides, pass layoutFull.
Playground
/api/authV2/open/device/:deviceId/canvas1// Generating...Request Body
Canvas API requests combine content data with a screen description. Use data for values the screen can read, and use windowData to describe the element structure. You can also pass taskAlias to label the item in the device task list, layoutFull to adjust the FULL layout, link to set the tap-to-open URL, and border to choose the screen border color.
To avoid conflicting meanings, do not use type, key, windowData, layoutFull, taskAlias, link, border, __proto__, constructor, or prototype as keys inside data.
{
"refreshNow": true,
"taskAlias": "Morning canvas",
"data": {
"title": "Canvas API",
"message": "Hello Dot."
},
"windowData": {
"default": [
{
"type": "div",
"props": {
"tw": "flex flex-1 bg-white text-black",
"children": "{{get inputData \"title\"}}"
}
}
]
},
"layoutFull": {
"tw": "p-0 bg-white",
"style": {
"padding": 0
}
},
"link": "https://dot.mindreset.tech",
"border": 0
}Writing windowData
windowDatamust contain adefaultlayer array;- Elements are usually written as
{ "type": "div", "props": { ... } }; props.twwrites Tailwind-like styles, whileprops.stylewrites explicit inline styles;divdefaults to aflexcontainer; when another display mode is needed, setblock,grid,hidden, or another display class inprops.tw, or setprops.style.display;props.childrencan be a string, one element object, or an array of elements;- Dynamic content can read request
datawith{{get inputData "user.name" default="-"}}; - List structures can use
$forinside the repeated node'sprops;$emptysits next to$forand replaces that node when the array is empty; - Common element types are
div,span, andimg; - Images may use data URIs or anonymously accessible
http(s)image URLs; - The outermost container does not need extra padding; when
layoutFullis omitted, the device FULL layout keeps its default spacing. UselayoutFull.tworlayoutFull.stylewhen you need to override the FULL layout; - The request is limited by template size and complexity to keep rendering stable.
List Structures: $for / $empty
$for expands elements during JSON compilation. It is not a browser JavaScript loop. Put it inside the props of the node to repeat; the repeated unit is that node itself. items is a JSON path read from the current template context; as and index inject local values for the loop body. limit is optional. When omitted, all rows are rendered, and the device layout decides how many fit on screen.
{
"type": "div",
"props": {
"$for": {
"items": "inputData.tasks",
"as": "task",
"index": "index"
},
"$empty": {
"type": "div",
"props": {
"children": "No tasks"
}
},
"children": "{{index}}. {{get task \"title\" default=\"\"}}"
}
}Conditions And Formatting
Use $ifAny / $then / $else for structural conditions that add, replace, or remove whole elements. $ifAny may be a single JSON path or an array of paths. If any value exists and has content, $then is used; otherwise $else is used.
{
"$ifAny": ["inputData.icon", "inputData.signature"],
"$then": {
"type": "div",
"props": {
"children": "{{get inputData \"signature\" default=\"\"}}"
}
},
"$else": {
"type": "div",
"props": {
"children": "No signature"
}
}
}For short text conditions inside a string, use template if / else syntax or compare. Use if to check whether a value has content, and use compare when two values must be compared explicitly:
{{#if (get inputData "title" default="")}}{{get inputData "title"}}{{else}}Untitled{{/if}}
{{#compare inputData.status "===" "done"}}Done{{else}}In progress{{/compare}}Use formatDate for dates and formatCompactNumber for numbers. For standard number formatting instead of compact K/M output, set the third argument to standard.
{{formatDate inputData.date "yyyy/MM/dd" "Asia/Shanghai"}}
{{formatDate inputData.date "EEE" "Asia/Shanghai"}}
{{formatCompactNumber inputData.count}}
{{formatCompactNumber inputData.count "en-US" "standard" 0}}Supported Styles And Custom Tailwind Classes
Canvas API supports a device-oriented static rendering style surface. In practice, the styling boundary is: React object-like elements + a static CSS subset, plus Dot.'s custom Tailwind font utilities, breakpoint handling, and image-processing classes.
| Syntax | Source | Notes |
|---|---|---|
props.style | Dot Canvas static style subset | Best for deterministic pixel values and explicit styles. |
props.tw | Tailwind-like syntax + Dot. extensions | Best for quick layout, color, spacing, font classes, and image-processing classes. |
layoutFull.tw / layoutFull.style | Dot. layout wrapper override | Use for full-bleed screens, background, and padding overrides. |
The props.style support list is aligned with the static CSS support table. Common categories include CSS variables, display, position, color, margin, padding, top/right/bottom/left, width/height, minWidth/minHeight/maxWidth/maxHeight, border*, borderRadius*, Flexbox, gap, fontFamily, fontSize, fontWeight, fontStyle, tabSize, textAlign, textIndent, textTransform, textOverflow, textDecoration, textShadow, letterSpacing, lineHeight, whiteSpace, lineClamp, wordBreak, textWrap, backgroundColor, backgroundImage, backgroundPosition, backgroundSize, backgroundClip, backgroundRepeat, transform, transformOrigin, objectFit, objectPosition, opacity, boxSizing, boxShadow, overflow, filter, clipPath, mask*, and WebkitTextStroke*.
This style list only describes props.style. The font classes, breakpoint processing, and img-* image-processing classes below are Dot. custom Tailwind extensions on top of props.tw.
Important boundaries: Canvas API is not a full browser environment. It does not support <style>, external <link>, or <script>; it does not support 3D transforms, z-index, or calc(); currentColor is only reliable on the color property; advanced typography features and RTL languages are not the current focus. Canvas API also does not support JSX, arbitrary JS expressions, React hooks, browser APIs, external CSS, or custom components.
Common props.tw classes include flex, flex-row, flex-col, flex-1, shrink-0, grow, items-*, justify-*, w-full, h-full, w-[84px], h-[40px], min-w-0, min-h-0, max-h-[200px], gap-*, gap-[5px], p-*, px-[8px], py-[5px], bg-*, text-*, border*, rounded*, overflow-hidden, box-border, box-content, fill-black, and fill-white. If a Tailwind class is uncertain, prefer props.style.
Text supports two font families of class patterns:
| Type | Syntax | Example |
|---|---|---|
| Regular font | text-{size}-{font} | text-18-chillduansans |
| Regular font with arbitrary px size | text-[Npx]-{font} | text-[20px]-playfairdisplay |
| Tailwind font size key | text-{xs/sm/base/lg/xl/2xl...}-{font} | text-lg-chillduansans |
| Pixel font | text-pixel-{size}[-variant] | text-pixel-12-zpix |
Common regular font keys: chillduansans, chillksans, chillorganic, chillroundf, chillroundgothic, logoscunboundedsans, maokenyingbikaishuj0.09, playfairdisplay, zihunzhoukesong.
Pixel font classes: text-pixel-8, text-pixel-8-quan, text-pixel-10, text-pixel-12, text-pixel-12-xiaoya, text-pixel-12-zpix, text-pixel-16, text-pixel-16-cusong, text-pixel-16-unifont, text-pixel-16-unifontmono, text-pixel-24.
For text layout, prefer these props.style fields: fontSize, fontWeight, lineHeight, letterSpacing, whiteSpace, wordBreak, lineClamp, textOverflow, and overflow. A multi-line body usually looks like:
{
"tw": "text-18-chillduansans",
"style": {
"lineHeight": "20px",
"whiteSpace": "pre-wrap",
"wordBreak": "break-word",
"overflow": "hidden"
}
}Image elements support extra e-ink image-processing classes in img.props.tw:
img-dither-none
img-dither-diffusion
img-dither-ordered
img-kernel-threshold
img-kernel-atkinson
img-kernel-burkes
img-kernel-floyd-steinberg
img-kernel-sierra2
img-kernel-stucki
img-kernel-jarvis-judice-ninke
img-kernel-diffusion-row
img-kernel-diffusion-column
img-kernel-diffusion-2d
img-levels-2
img-levels-3
img-levels-4
img-levels-8
img-levels-16For image layout, use style.objectFit and style.objectPosition, such as contain / center bottom. Avoid relying on aspect-ratio, CSS Grid, absolute positioning, pseudo classes, media queries, external CSS, scripts, or custom components; these are not part of the stable Canvas API surface.
Response
{
"message": "Device ABCD1234ABCD Canvas API content switched."
}Common Error Examples
Unsupported element type:
{
"windowData": {
"default": [
{
"type": "button",
"props": {
"children": "Bad type"
}
}
]
}
}Response:
{
"message": "The canvas element button for device ABCD1234ABCD is not supported (path: windowData.default[0].type). Only div, span, and img are currently supported."
}Unsupported property:
{
"windowData": {
"default": [
{
"type": "div",
"props": {
"children": [
{
"type": "span",
"props": {
"onClick": "alert(1)",
"children": "Unsafe"
}
}
]
}
}
]
}
}Response:
{
"message": "The canvas property onClick for device ABCD1234ABCD is not supported (path: windowData.default[0].props.children[0].props.onClick)."
}Invalid image source:
{
"windowData": {
"default": [
{
"type": "div",
"props": {
"children": [
{
"type": "img",
"props": {
"src": "data:image/png;base64,not-a-png"
}
}
]
}
}
]
}
}Response:
{
"message": "The canvas image src for device ABCD1234ABCD is invalid (path: windowData.default[0].props.children[0].props.src). Provide data:image/*;base64,... or an anonymously accessible http(s) image URL."
}Did this solve your problem?
Join our community