Formatting Slack Messages¶
Slack does not render HTML — send it <b>CRITICAL</b> and readers see the literal tags. Instead it has two native formats, both fully supported by pg_relay because the webhook transport delivers the message payload verbatim: mrkdwn, Slack's markdown dialect, for formatting inside any text; and Block Kit, structured JSON layout for messages with real visual structure — headers, aligned fields, severity colors, buttons. This chapter is the reference for both, written around the alerting use case: a status, details, fix steps, and a link to a complete guide.
mrkdwn — formatting inside text¶
Anything that ends up in a Slack text field (or a mrkdwn text object in Block Kit) understands:
| You write | Slack shows |
|---|---|
*bold* |
bold |
_italic_ |
italic |
~strike~ |
~~strike~~ |
`code` |
code (inline, monospace, highlighted) |
```multi-line``` |
a code block — ideal for diagnostic SQL |
> quoted line |
an indented quote bar |
:rotating_light: :warning: :white_check_mark: |
🚨 ⚠️ ✅ |
| a literal newline | a line break |
Three characters are special and must be escaped when they appear as content: & as &, < as <, > as > — because <...> is how Slack encodes links (below). There is no automatic list rendering; write numbered steps as plain 1. / 2. lines, which reads perfectly well.
Links — including deep links to complete guides¶
| You write | Slack shows |
|---|---|
https://example.com/guide |
the bare URL, auto-linked |
<https://example.com/guide> |
the same, explicitly |
<https://example.com/guide\|Fix procedure> |
Fix procedure as a clickable label |
The labelled form is the one to reach for in alerts: a long catalog URL stays out of the visual flow, and the label can carry the check id so the message reads like documentation. Anchors survive intact — a deep link such as
<https://pg-health-framework.pebbleit.com.au/latest/catalog/checks/c07-toast-corruption-detection/#pghf07-005-orphaned-toast-tables|PGHF07-005 — Orphaned TOAST tables (fix procedure)>
lands the reader on that exact section of the guide, not the top of the page. A useful pattern for operational alerts is both forms together: the labelled link for clicking, and the bare URL on the next line for copy/paste into a ticket.
Slack may expand a bare link into a preview card (an "unfurl"). For alert traffic that bloats the channel — set "unfurl_links": false in the message payload to keep links clickable but compact.
Block Kit — structured layout¶
Block Kit is a blocks array in the chat.postMessage payload. For alerts, the essential blocks:
| Block | Renders as | Alert use |
|---|---|---|
header |
large bold title bar (plain text + emoji, ≤150 chars) | 🚨 PG Healthcheck — CRITICAL |
section with fields |
up to 10 short mrkdwn cells in two aligned columns | Alert / Status / Check / Database |
section with text |
a paragraph of mrkdwn (≤3000 chars) | the details, the fix steps |
divider |
a horizontal rule | separating summary from detail |
actions with button |
clickable buttons; a url button opens a link; style primary (green) or danger (red) |
Open fix procedure → the guide's deep link |
context |
small muted text/icons | alert id, source system, timestamp |
Two framing details complete an alert:
- The severity sidebar — the colored bar down the message's left edge — comes from wrapping the blocks in an
attachmentsentry with acolor: for example#d61111for CRITICAL,#e8a317for WARNING,#2eb67dfor OK. - Always set a top-level
textwith a one-line summary. It is the fallback shown in desktop and mobile push notifications, and anywhere blocks cannot render.
A complete healthcheck alert¶
This payload — delivered verbatim by the Processor — renders with a red sidebar, a header bar, two columns of fields, a divider, detail sections with a SQL code block, a red button into the fix guide, and a muted footer:
{
"channel": "C0XXXXXXX",
"text": "CRITICAL: Orphaned TOAST tables on relay_dev (PGHF07-005)",
"unfurl_links": false,
"attachments": [{
"color": "#d61111",
"blocks": [
{"type": "header",
"text": {"type": "plain_text", "text": "🚨 PG Healthcheck — CRITICAL", "emoji": true}},
{"type": "section", "fields": [
{"type": "mrkdwn", "text": "*Alert:*\nOrphaned TOAST tables"},
{"type": "mrkdwn", "text": "*Status:*\n`CRITICAL`"},
{"type": "mrkdwn", "text": "*Check:*\n`PGHF07-005`"},
{"type": "mrkdwn", "text": "*Database:*\n`relay_dev`"}]},
{"type": "divider"},
{"type": "section", "text": {"type": "mrkdwn",
"text": "*Details*\n2 TOAST tables have no owning relation — debris from an interrupted `DROP TABLE`. They consume disk and are scanned by autovacuum for no benefit."}},
{"type": "section", "text": {"type": "mrkdwn",
"text": "*How to fix*\n1. Identify the orphans:\n```SELECT c.oid::regclass FROM pg_class c WHERE c.relkind = 't' AND NOT EXISTS (SELECT 1 FROM pg_class t WHERE t.reltoastrelid = c.oid);```\n2. Follow the catalog's drop procedure"}},
{"type": "actions", "elements": [
{"type": "button", "style": "danger",
"text": {"type": "plain_text", "text": "Open fix procedure"},
"url": "https://pg-health-framework.pebbleit.com.au/latest/catalog/checks/c07-toast-corruption-detection/#pghf07-005-orphaned-toast-tables"}]},
{"type": "context", "elements": [
{"type": "mrkdwn", "text": "Alert id `HC-2215` • pg-health-framework • 2026-08-22 05:20 UTC"}]}
]
}]
}
Slack's structural limits are generous for alerting: up to 50 blocks per message, 3000 characters per text object, 10 fields per section. If a message ever needs more than that, it has stopped being an alert — link to the complete guide instead, which is the point of the button.
Where the formatting happens¶
The Processor never composes or interprets any of this — it posts the notifier-rendered message verbatim, which is why every Slack feature works without pg_relay changes. Composition lives in pg_relay_notifier: plain body_text (with any mrkdwn in it) becomes the message text, and its Slack provider renders or passes through Block Kit payloads for the structured layouts above — see its own documentation for the composition API.
Continue to Mattermost — the same recipe pattern pointed at a self-hosted, Slack-compatible server that works with no internet connection at all.