Message Format Reference
Every Markdown construct, token, block, mark, and color you can use in an incoming webhook body
This page lists everything you can put in an incoming webhook body. The text form uses Markdown and tokens; the blocks form uses block JSON. Request format and limits are on the Incoming Webhooks page.
What each form can express
| Construct | text | blocks |
|---|---|---|
| Bold · italic · strikethrough · underline · inline code · link | Yes | Yes |
| List · task · quote · code block · table | Yes | Yes |
| Text color · background color | No | Yes |
| Toggle (collapsible) | No | Yes |
| Issue · change request · document · project · repository · goal · doc folder · pipeline run · channel reference | Yes | Yes |
| Sprint reference | No | Yes |
| Date | Yes | Yes |
| Heading · divider | Shown unformatted | Shown unformatted |
| Image | No | No |
Chat messages show headings as plain text without heading size, and do not draw dividers. Use bold instead of a heading when you want emphasis. Images are blocks that point to an image uploaded to CollabOps, so a webhook cannot send them. Link to external images instead.
The text form — Markdown
CommonMark is supported, plus tables and strikethrough.
| Write | Result |
|---|---|
**bold** | Bold |
*italic* | Italic |
~~strike~~ | Strikethrough |
<u>underline</u> | Underline. This is the only HTML tag interpreted; other tags show as plain text. |
| Text wrapped in a pair of backticks | Inline code |
[label](https://collabops.ai) | Link |
A line starting with # to ###### | Heading, levels 1 to 6. Shown as plain text in chat. |
A line starting with - | Bulleted list. Each two-space indent goes one level deeper (up to 5 levels). |
A line starting with 1. | Numbered list |
A line starting with - [ ] or - [x] | Task list (unchecked or checked) |
A line starting with > | Quote |
A line containing only --- | Divider. Not drawn in chat. |
| A block fenced by three backticks | Code block. You can name the language after the opening backticks. |
| GFM table syntax | Table. : in the separator row sets column alignment. |
| A single line break | A new line within the same paragraph. |
| A blank line | A new paragraph. |
 | Images are not shown. |
Text color, background color, and toggles have no Markdown syntax, so they are available only in the blocks form.
Tokens
Tokens inside text become issue chips and date chips. They all share one shape.
{{kind:key=value|key=value}}Key order does not matter. A token with an unknown kind or a missing required key shows up as the literal text you wrote, with no error. When you start using a new token, check once in the channel that it turned into a chip.
Reference chips — issues, change requests, docs, folders, runs, channels and more
{{issue:slug=acme|key=42|label=Login error}}
{{cr:slug=acme|key=318|label=Fix session expiry}}
{{repo:slug=acme|key=7|label=web}}
{{run:slug=acme|key=128|label=nightly}}| Kind | Refers to |
|---|---|
issue | Issue |
cr | Change request |
doc | Document |
project | Project |
repo | Repository (not repository) |
goal | Goal |
folder | Doc folder |
run | Pipeline run |
channel | Chat channel |
| Key | Required | Description |
|---|---|---|
slug | Yes | Workspace slug, the value in https://collabops.ai/<slug>/…. |
key | Yes | The number. For issue #42 it is 42. Only channel takes the channel ID (a UUID) instead of a number. |
label | Recommended | Title shown on the chip. The chip does not look up the real title; it shows this value as is, so include it. |
Sprint references cannot be made with text tokens. {{sprint:…}} and {{repository:…}} stay as literal text. Write repositories as repo, and use the blocks form when you need a sprint.
Date
{{date:iso=2026-09-23|hasTime=0}}
{{date:iso=2026-09-23T15:30:00+09:00|hasTime=1}}| Key | Required | Description |
|---|---|---|
iso | Yes | YYYY-MM-DD or an ISO 8601 timestamp. Include the time zone offset for timestamps. An unparseable value stays as literal text. |
hasTime | No | 1 shows the time as well; omitted or 0 shows the date only. |
Things to watch with tokens
Do not put tokens inside a code block. They do not become chips, and an internal placeholder string is shown instead.
A token inside bold or inline code still becomes a chip, and the surrounding formatting is dropped.
To show token-shaped text literally, escape the opening braces with backslashes. \{\{issue:…}} is shown as {{issue:…}} instead of a chip.
The blocks form
blocks is a flat array of block objects, rendered in array order. Blocks are never nested; toggles use parentId and tables use tableId to express relationships.
| Field | Required | Description |
|---|---|---|
id | Yes | A string unique within this message |
type | Yes | Block type (see below) |
attrs | Depends on type | Block attributes |
children | Yes | Array of inline pieces. Use an empty array [] for blocks without text |
parentId | No | For a block inside a toggle, that toggle's id |
Block types
type | attrs | Description |
|---|---|---|
paragraph | None | Paragraph |
heading | level 1–6 (required) | Heading. Shown as plain text in chat. |
listItem | kind bullet · ordered · task (required), indent 0–5 (required), checked (task), start (first item of a numbered list) | List item. A following item with a larger indent becomes its child. |
blockquote | None | Quote |
codeBlock | language (optional) | Code block. Put one unformatted text piece in children and separate lines with \n. |
thematicBreak | None | Divider. children is []. Not drawn in chat. |
toggle | None | Collapsible. children is the title shown while collapsed; blocks shown when expanded point to this toggle with parentId. It starts collapsed and expands on click. |
tableCell | tableId · rowIdx · colIdx (required), colCount · columnAlign (first cell of the table only) | Table cell. Consecutive cells with the same tableId form one table. The first row is the header. |
table · image · columnList · column | Blocks for the document editor. Build tables with tableCell; images cannot be sent by webhook. |
Inline pieces (children)
children mixes text pieces and chip pieces. Text pieces take formatting through marks.
[
{"text": "Deployed ", "marks": [{"type": "bold"}]},
{"type": "embed", "embedType": "entityRef",
"attrs": {"kind": "issue", "workspaceSlug": "acme", "entityKey": 42, "title": "Login error"}},
{"type": "embed", "embedType": "date",
"attrs": {"iso": "2026-09-23", "hasTime": false}}
]embedType | attrs |
|---|---|
entityRef | kind · workspaceSlug · entityKey (integer; a channel ID string for channel) · title (all required) |
date | iso · hasTime (both required) |
entityRef accepts 10 kind values: issue · cr · doc · project · repository · goal · sprint · folder · run · channel. Unlike tokens, repositories are written as repository, and sprints are allowed. For a sprint, entityKey is the sprint group number. The chip shows title as is.
Marks
type | attrs | Effect |
|---|---|---|
bold | Bold | |
italic | Italic | |
strikethrough | Strikethrough | |
underline | Underline | |
code | Inline code | |
link | url (required) | Link |
textColor | color (required) | Text color |
bgColor | color (required) | Background color |
A piece can carry several marks. For example, [{"type": "bold"}, {"type": "link", "attrs": {"url": "https://collabops.ai"}}] is a bold link.
Colors
color accepts only seven names: gray · amber · green · blue · coblue · purple · red. The actual shade for light and dark themes is decided by the app. Any other value, including hex codes, returns 422. To have no color, leave the value out.
Block-level attrs.textColor · attrs.bgColor are accepted without error but are not painted in chat messages. To show color in chat, put a textColor · bgColor mark on the text piece. To color a table cell, put the mark on the text inside the cell, not on the cell's attrs.
Validation
blocks are validated in full before posting. If any block breaks the spec, the whole message is rejected with 422, and error.title names the block by index (from 0), as in blocks.3 rejected.