Zudo Slack Wisdom
GitHub repository

Type to search...

to open search from anywhere

Canvas

The canvases.create / canvases.edit write path, channel canvases, the access.set read flow, and hard limits.

A canvas is a markdown document surface built into Slack — "simple but powerful documents" that live either attached to a channel or on their own (surfaces/canvases). This page covers the bot-facing write path: creating one, keeping it in sync, granting read-only access, and where its hard limits are. For how Canvas ranks against the other four data surfaces for a read-only dashboard, see Choosing a Dashboard Surface.

Creating a canvas

canvases.create takes an optional title, an optional document_content (markdown), and an optional channel_id (reference/methods/canvases.create). Passing channel_id is what makes it a channel canvas: the canvas "may be automatically added to a channel tab" in that channel's header (surfaces/canvases), rather than existing as a standalone document the bot has to share out by link. Both canvases.create and canvases.edit require the canvases:write scope (reference/scopes/canvases.write).

{
  "channel_id": "C0123456789",
  "title": "Sync Status",
  "document_content": {
    "type": "markdown",
    "markdown": "# Sync Status\n\nLast run: ..."
  }
}

Editing a canvas — one operation per call

canvases.edit's own docs state it plainly: "Only one operation per API call is currently supported" (reference/methods/canvases.edit). The request shape is a changes array, and that array can carry several change objects — but each one is still a single atomic operation:

  • insert_after / insert_before — add content relative to an existing section

  • insert_at_start / insert_at_end — add content at either end of the document

  • replace — overwrite a section

  • delete — remove a section

  • rename — change the canvas title

Because there's no cheap "patch this one cell" operation, the refresh pattern that stays sane for a bot-owned canvas regenerated from source data is a whole-document replace: build the full markdown string on every tick and send it as one replace operation, rather than trying to diff the previous document and send incremental changes.

{
  "canvas_id": "F0123456789",
  "changes": [
    {
      "operation": "replace",
      "section_id": "<root-section-id>",
      "document_content": {
        "type": "markdown",
        "markdown": "# Sync Status\n\nLast run: ..."
      }
    }
  ]
}

Granting read-only access

canvases.access.set takes a canvas_id, an access_level of read, write, or owner, and either channel_ids or user_ids — not both (reference/methods/canvases.access.set). The order matters: Slack's docs say to "share the canvas link in the channel or with the user you are trying to set access levels for, respectively" — for the user_ids form specifically, "you must have sent the user the canvas directly first," or the access.set call fails. Share the canvas before you set access on it, not after.

Read access is a real ceiling, not just a UI hint. Someone holding view-only access to a canvas "cannot make changes or comments" (Manage access permissions for canvases and lists) — canvases don't have the carve-out Lists has, where a viewer can still comment on an item.

Persistence as a channel tab

A canvas created (or later attached) with a channel_id sits in the channel header as its own tab, alongside Messages — durable UI real estate a pinned message doesn't get. It doesn't scroll out of view as the channel gets used, and there's no equivalent of a channel member accidentally unpinning it.

Limits

LimitValueSource
Operations per canvases.edit call1canvases.edit
document_content markdown size1 MiB / 1,048,576 characters — applies per change when a changes array has more than one entrycanvases.edit, surfaces/canvases
Table cells per table300 — any mix of rows x columnssurfaces/canvases
canvases.edit rate limitTier 3 (50+ requests/min)canvases.edit
Required scopecanvases:write — create, edit, and access.set all need itscopes/canvases.write

What a canvas can't do

No board layout, no columns, no per-cell writes. If the content is really a status board rather than a document, see Choosing a Dashboard Surface for the other four options.

Revision History

CreatedUpdated