Templates

The notification system uses Jinja2 templates to format messages for each delivery driver. Every notification — whether it goes to Slack, email, PagerDuty, or a generic webhook — is rendered through a template before dispatch.

Templates live in apps/notify/templates/ as .j2 files. If Jinja2 is not installed, the system falls back to Python’s str.format_map for basic {variable} substitution, but all built-in templates require Jinja2.

How Templates Are Resolved

When a driver renders a notification, the system looks for a template in this order:

Priority Source Example
1 Config key (template, text_template, or payload_template) {"template": "file:my_custom.j2"}
2 Default driver file: <driver>_text.j2 slack_text.j2
3 Default driver file: <driver>_payload.j2 generic_payload.j2
4 Default driver file: <driver>.j2 slack.j2

For HTML (email only), the system checks html_template in config, then falls back to <driver>_html.j2. HTML is optional — if no HTML template is found, only the text version is sent.

Template Spec Formats

The template config value accepts several formats:

# File reference (explicit)
"file:slack_text.j2"

# File reference (auto-detected — if a matching file exists in templates/)
"slack_text.j2"

# Inline template string
"Alert: {{ title }} — Severity: {{ severity }}"

# Dict spec
{"type": "file", "template": "my_custom.j2"}
{"type": "inline", "template": "Alert: {{ title }}"}

If a string matches an existing file in apps/notify/templates/, it is treated as a file reference automatically. Otherwise it is rendered as an inline template.

Available Templates

email_html.j2

HTML email with severity-colored left border and badge. Escapes all user content with | e for XSS safety.

Sections: Title with color-coded severity badge, incident ID, source/timestamp, message body, intelligence summary, probable cause, recommended actions, recommendations (up to 10), tags, context, ingest summary, check summary.

Severity colors: critical = red, warning = yellow, info = teal, success = green.

email_text.j2

Plain-text email fallback. Same sections as the HTML template in a readable text format. Uses | upper for severity, | default() for safe recommendation access.

slack_text.j2

Slack Block Kit JSON payload. Includes a sanitize macro that escapes backticks, converts **bold** to Slack’s *bold* syntax, strips carriage returns, and truncates long content.

Body selection logic: Prefers intelligence.summary or intelligence.probable_cause over raw message when intelligence data is available.

Recommendations: Capped at 5, rendered as bullet points with bold titles.

generic_payload.j2

Structured JSON for generic webhooks. Uses | tojson filters throughout for safe JSON encoding. Includes recommendations (up to 10) and conditional intelligence fields.

pagerduty_payload.j2

PagerDuty Events API v2 format. Populates summary, severity, source, component, group, class, and custom_details. Conditionally includes system metrics (cpu_count, ram_total_human, disk_total_human) when available.

incident_notification.j2

Markdown-formatted notification. Uses headings, bold, code spans, and blockquote formatting for recommendations. Includes ingest and check summaries with “N/A” fallback when missing.

Context Variables

Every template receives the same context. The system builds this from the notification message and composed incident details.

Top-Level Variables

Variable Type Description
title string Alert title
message string Alert message body
severity string One of: critical, warning, info, success
channel string Notification channel name
tags dict Key-value metadata (source, component, group, etc.)
context dict Full context from the pipeline (check results, raw data)
incident dict Composed incident details (see below)
intelligence dict or None AI analysis results
recommendations list or None Normalized recommendation list
incident_id string or None Incident identifier
source string or None Alert source system

The incident Object

Composed by NotificationTemplatingService.compose_incident_details() from message data and live system metrics:

Field Type Description
incident.title string Alert title
incident.message string Alert message
incident.severity string Severity level
incident.channel string Channel name
incident.tags dict Tags from alert
incident.context dict Full context dict
incident.cpu_count int or None CPU count (from context or psutil)
incident.ram_total_bytes int or None Total RAM in bytes
incident.ram_total_human string or None Total RAM formatted (e.g., “16.0 GB”)
incident.disk_total_bytes int or None Total disk in bytes
incident.disk_total_human string or None Total disk formatted (e.g., “500.1 GB”)
incident.incident_id string or None Incident ID
incident.source string or None Source system
incident.environment string or None Environment name
incident.ingest dict or None Ingest stage summary
incident.check dict or None Check stage summary
incident.intelligence dict or None Intelligence stage results
incident.recommendations list Normalized recommendations
incident.recommendations_pretty string Pretty-printed recommendations text
incident.generated_at string ISO 8601 UTC timestamp

The intelligence Object

When AI analysis runs, intelligence contains:

Field Type Description
intelligence.summary string One-line analysis summary
intelligence.probable_cause string Root cause assessment
intelligence.actions list[string] Recommended actions
intelligence.recommendations list[dict] Detailed recommendations

The recommendations List

Each item in recommendations can be a dict or a plain string. Dict items typically contain:

Field Type Description
r.title string Short recommendation title
r.description string Detailed explanation
r.priority string Priority level (used in incident_notification.j2)
r.name string Alternative to title (used in slack_text.j2)
r.detail string Alternative to description (used in slack_text.j2)

The system normalizes recommendations from various formats (dict-of-dicts, single dict, scalar) into a list before passing to templates.

Customizing Templates

Override via NotificationChannel Config

Set a custom template in the channel’s config field in Django admin (/admin/notify/notificationchannel/):

{
  "webhook_url": "https://hooks.slack.com/...",
  "template": "file:my_slack.j2"
}

Or use an inline template directly:

{
  "webhook_url": "https://example.com/webhook",
  "template": "ALERT: {{ title }} [{{ severity | upper }}] — {{ message }}"
}

For email drivers, you can set both text and HTML templates:

{
  "smtp_host": "smtp.example.com",
  "text_template": "file:my_email_text.j2",
  "html_template": "file:my_email_html.j2"
}

Override via Pipeline Definition

In definition-based pipelines, pass template config in the notify node:

{
  "id": "notify",
  "type": "notify",
  "config": {
    "drivers": ["slack"],
    "template": "file:my_slack.j2"
  }
}

Writing Custom Templates

File Location

Place custom templates in apps/notify/templates/. The file must have a .j2 extension (or be referenced with the exact filename).

Minimal Example

{# my_webhook.j2 — minimal JSON webhook payload #}
{
  "text": {{ title | tojson }},
  "level": "{{ severity | upper }}",
  "body": {{ message | tojson }}
}

Accessing Nested Data Safely

Use Jinja2’s is mapping, is defined, and | default() to guard against missing data:

{# Safe intelligence access #}
{% if intelligence is mapping and intelligence.summary %}
  Summary: {{ intelligence.summary }}
{% endif %}

{# Safe recommendation iteration with limit #}
{% if recommendations %}
  {% for r in recommendations[:5] %}
    - {{ r.title | default('Untitled') }}: {{ r.description | default('') }}
  {% endfor %}
{% endif %}

{# Default values for optional fields #}
Source: {{ source | default('unknown') }}

Producing JSON Output

When your template outputs JSON (for Slack, PagerDuty, or webhook payloads), always use | tojson to escape strings:

{# CORRECT — tojson handles quoting and escaping #}
{"title": {{ title | tojson }}, "severity": {{ severity | tojson }}}

{# WRONG — breaks if title contains quotes or special characters #}
{"title": "{{ title }}", "severity": "{{ severity }}"}

HTML Templates

For HTML output, use | e to escape user content and prevent XSS:

<h2>{{ title | e }}</h2>
<p>{{ message | e }}</p>

Jinja2 Quick Reference

Filters and patterns used across the built-in templates:

Pattern Usage
{{ var | upper }} Uppercase string
{{ var | tojson }} JSON-safe encoding (adds quotes, escapes)
{{ var | e }} HTML entity escaping
{{ var | default('fallback') }} Default for missing/falsy values
{{ var | trim }} Strip whitespace
{% if var %}...{% endif %} Conditional block
{% for item in list %}...{% endfor %} Loop
{% for k, v in dict.items() %} Dict iteration
{{ list[:5] }} Slice (limit items)
{% if var is mapping %} Type check (dict)
{% if var is string %} Type check (string)
{% if var is defined %} Check if variable exists
{% set x = 'value' %} Variable assignment
{% macro name(arg) %}...{% endmacro %} Reusable macro
{{ loop.last }} True on last loop iteration
{# comment #} Template comment (not in output)

This site uses Just the Docs, a documentation theme for Jekyll.