Template Editor
The template editor lets you create and customize message templates with a visual editor and live previews for each supported platform.
Creating a Template
- Navigate to Templates → Create Template
- Enter a name
- The editor opens with a default template structure
Editor Layout
The template editor has two main panels:
- Left: Platform Tabs + Config Editor — switch between platform tabs (Discord, Slack, Telegram, Google Chat, Webex, Default) and edit the JSON config for each with syntax highlighting
- Right: Live Preview — see a rendered preview for the active platform tab as you type. Advanced template expressions (such as
index,rangeover array elements,{{with}}, and.Metafields) render in the preview just as they will at delivery, with the few exceptions listed below. If a template expression is invalid, the preview shows the template error instead of silently leaving the raw{{…}}text — fix the expression and the error clears.
Embed field values can span multiple lines — press Enter inside the value editor to add a line break (for example, one bullet per item when looping over an array). Field names stay single-line.
Advanced template expressions run on Dispatch's servers, so the preview supports a defined subset of the template language and tells you plainly when a template falls outside it. Field access, {{if}}, {{range}}, {{with}}, pipelines, index, slice, len, printf, urlquery, the comparison functions and every platform helper are all supported.
Simple field substitutions render instantly in your browser without a server round-trip. In rare cases their preview can differ slightly from the delivered message — very large numbers, len, json key ordering, short truncate widths, and .Payload.* fields referenced inside a {{range}} or {{with}} block (those preview as the payload value but deliver as <no value> or a template error — use the loop item's own fields instead) — while delivery always uses the real engine.
These are not, and show a "not supported in preview" message — they still work when the message is delivered:
- Variables declared outside a
{{range}}or{{with}}header ({{$x := …}}), and assignment ({{$x = …}}) — naming the subject, as in{{with $pr := .Payload.pull_request}}, is supported print,println,html,js,call— useprintfprintfargument indexes (%[1]s) and*widths — ordinary verbs and widths are finedefine,template,block- Looping over a computed value (
{{range len .Payload.x}}), or more than two nested loops
JSON numbers are floating point, so {{.Payload.amount}} renders a large value as 1.5e+06. Use {{fixed 0 .Payload.id}} for a whole number and {{fixed 2 .Payload.total}} for a money amount. Stripe, PayPal and Shopify send amounts in cents, so divide first: {{fixed 2 (div 100 .Payload.amount)}} renders 1200 as 12.00. This affects delivered messages too, so it is worth checking any existing template that renders an ID, timestamp, or amount.
So the preview stays responsive, it trims arrays and objects longer than 100 entries and leaves out anything nested deeper than 32 levels, telling you when it has. Delivery always uses your full payload. To preview the entire payload as JSON, use {{json .Payload}} — that renders identically in the preview and at delivery.
Platform Tabs
Each tab represents a platform variant within a v2 multi-platform template:
| Tab | Format | Description |
|---|---|---|
| Discord | Embed config | Standard Discord embed with title, description, fields, color |
| Slack | Block Kit JSON | Native Slack Block Kit blocks for rich Slack messages |
| Telegram | HTML template | HTML string for Telegram's HTML parse mode |
| Google Chat | Text template | Google Chat formatted text (*bold*, _italic_, `code`) |
| Webex | Markdown template | Webex markdown (**bold**, *italic*, `code`, [text](url)) |
| Default | Embed config | Fallback used when no platform-specific variant is configured |
At delivery time, Dispatch selects the variant matching the destination type. If no matching variant is set for that platform, the Default variant is used.
You don't need to configure all platforms. Configure only the tabs you need — leave the rest empty and Dispatch will fall back to auto-formatting or the Default tab.
Sample Sets
Alongside the sample payload you are editing — the active sample, the one the live preview renders with — Dispatch stores a set of up to 10 sample events.
- Save current payload as sample adds whatever is in the editor to the set.
- Add from recent events opens a picker of the events your project received in the last 30 days, newest first — each labelled by its event type, or a short id when the source doesn't set one, with events your filters blocked marked filtered. A filter box above the list narrows it as you type; press ↓ to move from the box into the list, then move with the arrow keys (Home and End jump to the ends), press Space to select one or several, and Enter to add them — with nothing selected, Enter adds the highlighted event. Events already in the set are shown as Added.
- Each stored sample can be loaded back into the editor as the active sample, or removed from the set.
Adding or removing a sample is an unsaved change — it takes effect when you save.
Creating a template from an event starts the set off with that event, unless the event is unusually large — in which case the template is still created, just without the first sample. A multi-row selection from the events table seeds one sample per selected event, up to the 10-sample cap — see Create from several events at once.
Storing several samples is worth doing when one source sends different event shapes — an opened pull request carries fields a closed one does not. AI generation reads the samples in the set on top of the payload in the editor, so the template it writes holds up across all of them.
Generating with AI
The Generate with AI button drafts a template from a plain-language description (for example, "Dependabot alert: severity, package, CVE link, red accent"). It grounds every {{.Payload.…}} reference in your samples — the payload in the editor and every sample in the set — so a field that only some of your events carry is kept rather than corrected away. A path found in none of them is auto-corrected when there is an obvious match, and flagged with a warning in the review dialog when there isn't, so you can fix it rather than discover a blank field later. The dialog names what it will read — the payload in the editor, however many samples your set holds, or neither — and trims a very large set to fit the request by dropping whole samples from the end. With the sample box cleared and no stored samples, it tells you generation has nothing but your description to work from. Generated values apply to the Default platform tab.
Inside the dialog, Ctrl+Enter (⌘Enter on macOS) generates and Esc closes it.
Discord Tab
The Discord tab uses the standard embed config format:
{
"title": "{{.Meta.EventType}}",
"description": "Event from {{.Payload.repository.full_name}}",
"color": 5763719,
"fields": [
{
"name": "Action",
"value": "{{.Payload.action}}",
"inline": true
}
],
"footer": "Dispatch",
"thumbnail": "{{.Payload.sender.avatar_url}}",
"timestamp": true
}
The live preview renders a Discord embed the way Discord destinations will receive it (see the preview note above for the few browser-side approximations).
Slack Tab
The Slack tab accepts a slack_blocks array containing native Block Kit blocks. All string values inside the blocks support template expressions:
{
"slack_blocks": [
{
"type": "header",
"text": {
"type": "plain_text",
"text": "{{upper .Meta.EventType}}"
}
},
{
"type": "section",
"fields": [
{
"type": "mrkdwn",
"text": "*Repository*\n{{.Payload.repository.full_name}}"
},
{
"type": "mrkdwn",
"text": "*Action*\n`{{.Payload.action}}`"
}
]
}
]
}
Telegram Tab
The Telegram tab accepts a telegram_html string that supports template expressions and is sent with Telegram's HTML parse mode:
{
"telegram_html": "<b>{{upper .Meta.EventType}}</b>\n\nRepo: <code>{{.Payload.repository.full_name}}</code>\nAction: <code>{{.Payload.action}}</code>\n\nBy <b>{{.Payload.sender.login}}</b>"
}
Supported HTML tags: <b>, <i>, <u>, <s>, <code>, <pre>, <a href="...">, <tg-spoiler>.
Variable Discovery
The editor helps you discover available template variables:
- A Variables panel shows all available
.Metavariables (event ID, event type, endpoint ID, timestamp) - If recent events exist for linked endpoints, the editor shows actual payload paths from real event data so you can click to insert them
This panel is available across all platform tabs.
Assigning Templates
Templates are assigned per endpoint-destination link, in either direction:
- From the source: go to Sources and open the routing page for the endpoint-destination link; the template is one of the link's defaults.
- From the template editor: click Attach in the editor header. The dialog lists every link in your project (arrow-key navigable) with what each currently uses, shows how many links already use it as their default, warns you when attaching would replace another template, and stays open so you can wire several links in one visit. Links to webhook destinations appear but can't be chosen — they always receive the raw payload, so templates don't apply to them. Attaching uses the last saved version of the template — save your edits first. Prefer a narrower rollout? Scope with a rule… opens the chosen link's routing editor with a draft rule already carrying this template, so it applies only to events the rule matches — the link's defaults stay as they are, and nothing takes effect until you save the rule.
A template assigned either way is the link's default; routing rules on the link can still override it for specific events. See Templates for details.
Keyboard Shortcuts
Ctrl+S (⌘S on macOS) saves the template you are editing, and Ctrl+Alt+P (⌘⌥P) opens AI generation. Both are shown in the tooltip of the button they trigger.
Ctrl+K (⌘K) opens the command palette, which lists both — plus everything else available on the page.
These are the default bindings — any shortcut can be rebound or unbound.