← Slack integration

Writing Slack canvases

A canvas is a document that lives inside Slack. Where a message scrolls away, a canvas stays put and gets edited — which makes it the right output for anything people return to: a runbook, a spec, an incident summary, a rolling status doc.

Daslab writes canvases through the Slack integration. There are two kinds, and the difference matters.

Standalone vs channel canvases

A standalone canvas is a free-floating document. Create it with slack_create_canvas. It belongs to the app that made it and is visible to nobody else until you grant access.

A channel canvas is the canvas tab attached to a channel, next to Messages. Create it with slack_create_channel_canvas. Everyone in the channel can see it, and there is exactly one per channel — a second call returns channel_canvas_already_exists, which means the channel already has a canvas and you should edit that one instead.

Channel canvases require a paid Slack plan. On the free tier, slack_create_channel_canvas returns team_tier_cannot_create_channel_canvases; standalone canvases still work.

Permissions

Canvas tools need the canvases:read and canvases:write scopes. A workspace connected before canvas support shipped does not have them, and every canvas call will fail until you reconnect it from scene settings — the tools say so rather than returning a bare missing_scope.

For a channel canvas, the bot also has to be able to see the channel: public channels work as-is, private channels need the bot invited.

Sharing a standalone canvas

A standalone canvas starts private to the app. To let people read or edit it:

slack_set_canvas_access(canvas_id, access_level: "write", channel_ids: "C0123ABCD")

Grant to channels or to users, not both in one call. read gives view-only; write lets recipients edit. Grant access first, then post the link — slack_create_canvas returns the canvas URL alongside its ID.

Content is markdown, not mrkdwn

Slack messages use mrkdwn — the dialect with bold, _italic_, and <url|link text>. Canvases do not. Canvas bodies take ordinary markdown:

# Deploy runbook

## Before you start
- [ ] Check the channel is quiet
- [ ] Confirm the release tag

| Step | Owner |
| ---- | ----- |
| Bundle | CI |
| Cut over | on-call |

Ping <@U0123ABCD> if the cutover stalls.

Headings, lists, checklists, tables, code blocks, dividers, links, and <@U…> user mentions all work. Writing mrkdwn into a canvas produces literal asterisks.

Editing a canvas

slack_edit_canvas applies one operation per call:
OperationNeedsEffect
insert_at_end / insert_at_startmarkdownAppend or prepend
insert_after / insert_beforesection_id, markdownInsert next to a section
replacemarkdown, optional section_idReplace one section, or the whole canvas if no section is given
deletesection_idRemove a section
renametitleRetitle the canvas

The targeted operations need a section ID, which you get from slack_lookup_canvas_sections. Sections are addressed by heading, so filter by level or by text:

slack_lookup_canvas_sections(canvas_id, section_types: "h2", contains_text: "Rollback")

Call it with neither filter to list every section. Section IDs are not stable across edits — look them up in the same pass that uses them, rather than caching them between runs.

For a canvas your agent owns end to end, replace with no section_id is usually simpler than patching sections: regenerate the body and overwrite.

Deleting

slack_delete_canvas is permanent — Slack has no undo and no trash for canvases. Prefer replacing the content of a canvas people already have linked over deleting and recreating it, which breaks every existing link.