By default, the webhook channel POSTs the canonical payload. When that shape doesn’t fit your receiver, paste a Handlebars template into the channel form’s Payload template field. Tallwatch renders it against the canonical variables and POSTs the result. This page covers the variable surface, the helpers we register, and worked examples for common receivers.Documentation Index
Fetch the complete documentation index at: https://docs.tallwatch.com/llms.txt
Use this file to discover all available pages before exploring further.
Variable reference
The template runs against the same JSON object documented on the Payload page. Use dot-notation to access nested fields.| Variable | Type | Example value |
|---|---|---|
{{event}} | string | incident.opened |
{{occurred_at}} | string (ISO 8601) | 2026-06-02T14:23:01.829Z |
{{org.id}} | string (UUID) | 9b8c7d6e-... |
{{org.name}} | string | Acme Corp |
{{org.slug}} | string | acme |
{{monitor.id}} | string (UUID) | 1a2b3c4d-... |
{{monitor.name}} | string | API healthcheck |
{{monitor.type}} | string | http |
{{monitor.url}} | string | https://api.acme.com/health |
{{monitor.tags}} | string[] | ["prod", "api"] |
{{incident.id}} | string | abc12345 |
{{incident.status}} | string | open / acknowledged / resolved |
{{incident.opened_at}} | string (ISO 8601) | 2026-06-02T14:21:30.500Z |
{{incident.acknowledged_at}} | string | null | null |
{{incident.resolved_at}} | string | null | null on open events |
{{incident.duration_sec}} | number | null | 1037 |
{{incident.failing_regions}} | string[] | ["fra1", "iad1", "sgp1"] |
Built-in helpers
Standard Handlebars plus a small set of project-specific helpers:| Helper | Use | Output |
|---|---|---|
{{join failing_regions ", "}} | Comma-join an array | fra1, iad1, sgp1 |
{{json this}} | Embed a value as JSON | {"id":"abc..."} |
{{upper monitor.type}} | Uppercase a string | HTTP |
{{#if incident.resolved_at}}…{{/if}} | Conditional block | renders only on resolved events |
{{#each failing_regions}}…{{/each}} | Loop over an array | typical Handlebars iteration |
{{@last}}, {{@first}}, {{@index}} | Loop context inside {{#each}} | standard Handlebars |
{{var}} HTML-escapes by default, {{{var}}} writes literally. JSON output ALWAYS wants the triple-stash form so quotes and braces aren’t escaped:
Worked examples
Slack-compatible body
If you want to POST to a Slack incoming webhook URL but get richer control than the native Slack channel offers, use a webhook channel with this template:Slack template
Datadog event API body
Push Tallwatch incidents into Datadog’s event stream for cross-correlation with your APM/RUM data:Datadog template
Minimal internal alert
If your internal ticketing system just needs a one-line subject and a URL back:Minimal template
Status page incident publisher
Re-broadcast Tallwatch incidents to your existing status page tool (e.g. a Statuspage.io webhook receiver):Statuspage template
Errors and debugging
`template_render_failed` in the dispatch history
`template_render_failed` in the dispatch history
Your Handlebars template is malformed. Common causes: unclosed
{{#if}} block, unbalanced quotes, unescaped braces in a JSON value. Open the channel form and re-test with Send test alert — the error message will show the line number.My receiver returns 400 'invalid JSON'
My receiver returns 400 'invalid JSON'
Almost always a double-stash (
{{x}}) where you needed triple-stash ({{{x}}}). When a value contains a special character (", &, <), the double-stash escapes it to an HTML entity and the resulting body is no longer valid JSON.Switch to triple-stash for all string-valued fields in your template.A field comes out as the literal `undefined`
A field comes out as the literal `undefined`
Handlebars renders a missing value as an empty string by default, NOT
undefined — so if you’re seeing the literal word undefined in your output, something on your receiver side is JSON-parsing the string "undefined" and printing it. Likely a layer between Tallwatch and the destination is corrupting the body.Open the dispatch history in Tallwatch and look at the rendered body to confirm what we actually sent.Resolved events fire the template but `failing_regions` is empty
Resolved events fire the template but `failing_regions` is empty
Correct behaviour. On
incident.resolved, the failing-regions array is empty because all regions have agreed the monitor is up. Wrap region-dependent template parts in {{#if incident.failing_regions.length}}:I want different templates for opened vs resolved
I want different templates for opened vs resolved
One channel = one template. To branch by event type inside the same template:Tallwatch registers an
eq helper for this purpose.When NOT to template
If your receiver accepts the canonical payload, leave the template blank. Reasons:- One less thing to maintain when the canonical payload gains a new field
- Template errors are caught at dispatch time, the canonical path can’t go wrong
- The dispatch history is easier to read when bodies are predictable