Rich messages
A channel post webhook renders its event as a message in the target
channel. When the payload is a blocks message — an object with a blocks
array — it renders as a rich, structured message (headings, sections, field
grids, dividers, context lines). Anything without a blocks array falls back to
a formatted payload preview.
The sender does the formatting; each block maps to the chat renderer. The format is compatible with common block-based message builders, so existing block JSON generally works unchanged.
Payload shape
Send a JSON body with a blocks array (up to 50 blocks) and an optional text
fallback:
text becomes the message's plain-text body (used for search and
notifications). Sign and send it like any other webhook request — see
Setup.
Block types
Every supported block, with a payload example.
header
A bold heading. text is a plain_text object (max 150 characters).
section (text)
A block of markdown text. text is a text object — use mrkdwn for the compact
syntax (see Text formatting).
section (fields)
A two-column label/value grid (up to 10 fields). Each field is a text object;
the common *Label*\nvalue idiom is split into a label and value.
divider
A horizontal rule.
context
Small, muted supporting text — timestamps, captions, provenance. elements is a
list of text objects and image elements (up to 10); images render as links.
image
An image reference. Rendered as an alt-text link (see Images).
markdown
Standard/CommonMark markdown, passed through — bold **x**, tables, fenced code,
task lists. Use this for AI/LLM-generated markdown.
actions
A row of buttons. Only buttons with a url render (as links); interactive
buttons (with an action_id and no url) are dropped.
rich_text
Structured formatted text — sections, lists, quotes, and preformatted code. Flattened to markdown on render.
Text formatting
There are two text conventions:
mrkdwntext objects (insection,context, …) use a compact syntax:*bold*,_italic_,~strike~,`code`,<https://url|label>links, and<@U…>/<#C…>mentions. These are translated to standard markdown on render.- The
markdownblock uses standard/CommonMark markdown (**bold**, tables, fenced code, task lists) and is passed through as-is.
Mentions render as inert text — a webhook cannot ping members or @channel.
Images
Image references are arbitrary remote URLs. To avoid leaking channel viewers'
IPs to the sender's host (and tracking pixels), remote images from webhooks are
not auto-loaded. image blocks, section image accessories, and context
images render as their alt text linked to the image URL instead.
Not a blocks payload?
Any payload without a blocks array renders as the event type plus a formatted
payload preview (pretty-printed JSON, or raw text). Rich messages are opt-in by
sending a blocks array.
Security
Block content is untrusted input and is sanitized before rendering:
- Only http(s) links are kept; other URL schemes are dropped.
- Mentions and control characters are neutralized.
- Text lengths, field counts, and block counts are bounded.
- Interactive elements are dropped; remote images are never auto-loaded.
- The message author is always the webhook endpoint — a block can't spoof a different author.
Connecting tools that can't sign
Tools like PostHog (HTTP Webhook destination), Datadog (Webhooks integration), and Grafana (webhook contact point) can't HMAC-sign the request, but they can template a custom JSON body and send a static header. To connect them:
- Create a channel-post webhook and set its verifier to Shared secret (header token) (Configuration).
- In the tool, set the request URL to the endpoint URL, add the header
Authorization: Bearer <endpoint-secret>, and set the body to ablockstemplate. For example (PostHog interpolation shown):
Swap the interpolation tokens for the tool's own (Datadog $…, Grafana
{{ .Vars.… }}). Because the body is a blocks array, it renders as a rich
message — no per-tool support needed on our side.