# Notifications Templates > **Imposed rule:** Work of this type or tasks of this type on this component must also be applied everywhere else it should be applied. Templates shape the payload rendered for each channel when a rule action fires. They are deterministic, locale-aware artefacts stored alongside rules so Notify.Worker replicas can render identical messages regardless of environment. --- ## 1. Template lifecycle 1. **Authoring.** Operators create templates via the API (`POST /templates`) or UI. Each template binds to a channel type (`Slack`, `Teams`, `Email`, `Webhook`, `Custom`) and a locale. 2. **Reference.** Rule actions opt in by referencing the template key (`actions[].template`). Channel defaults apply when no template is specified. 3. **Rendering.** During delivery, the worker resolves the template (locale fallbacks included), executes it using the safe Handlebars-style engine, and passes the rendered payload plus metadata to the connector. 4. **Audit.** Rendered payloads stored in the delivery ledger include the `templateId` so operators can trace which text was used. --- ## 2. Template schema reference | Field | Type | Notes | |-------|------|-------| | `templateId` | string | Stable identifier (UUID/slug). | | `tenantId` | string | Must match the tenant header in API calls. | | `channelType` | [`NotifyChannelType`](../modules/notify/architecture.md#5-channels--connectors-plug-ins) | Determines connector payload envelope. | | `key` | string | Human-readable key referenced by rules (`tmpl-critical`). | | `locale` | string | BCP-47 tag, stored lower-case (`en-us`, `bg-bg`). | | `body` | string | Template body; rendered strictly without executing arbitrary code. | | `renderMode` | enum | `Markdown`, `Html`, `AdaptiveCard`, `PlainText`, or `Json`. Guides connector sanitisation. | | `format` | enum | `Slack`, `Teams`, `Email`, `Webhook`, or `Json`. Signals delivery payload structure. | | `description` | string? | Optional operator note. | | `metadata` | map | Sorted map for automation (layout hints, fallback text). | | `createdBy`/`createdAt` | string?, instant | Auto-populated. | | `updatedBy`/`updatedAt` | string?, instant | Auto-populated. | | `schemaVersion` | string | Auto-upgraded on persistence. | Templates are normalised: string fields trimmed, locale lower-cased, metadata sorted to preserve determinism. --- ## 3. Variables, helpers, and context Templates receive a structured context derived from the Notify event, rule match, and rendering metadata. | Path | Description | |------|-------------| | `event.*` | Canonical event envelope (`kind`, `tenant`, `ts`, `actor`). | | `event.scope.*` | Namespace, repository, digest, image, component identifiers, labels, attributes. | | `payload.*` | Raw event payload (e.g., `payload.verdict`, `payload.delta.*`, `payload.links.*`). | | `rule.*` | Rule descriptor (`ruleId`, `name`, `labels`, `metadata`). | | `action.*` | Action descriptor (`actionId`, `channel`, `digest`, `throttle`, `metadata`). | | `policy.*` | Policy metadata when supplied (`revisionId`, `name`). | | `topFindings[]` | Top-N findings summarised for convenience (vulnerability ID, severity, reachability). | | `digest.*` | When rendering digest flushes: `window`, `openedAt`, `itemCount`. | Built-in helpers mirror the architecture dossier: | Helper | Usage | |--------|-------| | `severity_icon severity` | Returns emoji/text badge representing severity. | | `link text url` | Produces channel-safe hyperlink. | | `pluralize count "finding"` | Adds plural suffix when `count != 1`. | | `truncate text maxLength` | Cuts strings while preserving determinism. | | `code text` | Formats inline code (Markdown/HTML aware). | Connectors may expose additional helpers via partials, but must remain deterministic and side-effect free. --- ## 4. Sample templates ### 4.1 Slack (Markdown + block kit) ```hbs {{#*inline "findingLine"}} - {{severity_icon severity}} {{vulnId}} ({{severity}}) in `{{component}}` {{/inline}} *:rotating_light: {{payload.summary.total}} findings {{#if payload.delta.newCritical}}(new critical: {{payload.delta.newCritical}}){{/if}}* {{#if topFindings.length}} Top findings: {{#each topFindings}}{{> findingLine}}{{/each}} {{/if}} {{link "Open report in Console" payload.links.ui}} ``` ### 4.2 Email (HTML + text alternative) ```hbs

{{payload.verdict}} for {{event.scope.repo}}

{{payload.summary.total}} findings ({{payload.summary.blocked}} blocked, {{payload.summary.warned}} warned)

{{#each topFindings}} {{/each}}
FindingSeverityPackage
{{this.vulnId}} {{this.severity}} {{this.component}}

{{link "View full analysis" payload.links.ui}}

``` When delivering via email, connectors automatically attach a plain-text alternative derived from the rendered content to preserve accessibility. --- ## 5. Preview and validation - `POST /channels/{id}/test` accepts an optional `templateId` and sample payload to produce a rendered preview without dispatching the event. Results include channel type, target, title/summary, locale, body hash, and connector metadata. - UI previews rely on the same API and highlight connector fallbacks (e.g., Teams adaptive card vs. text fallback). - Offline Kit scenarios can call `/internal/notify/templates/normalize` to ensure bundled templates match the canonical schema before packaging. --- ## 6. Best practices - Keep channel-specific limits in mind (Slack block/character quotas, Teams adaptive card size, email line length). Lean on digests to summarise long lists. - Provide locale-specific versions for high-volume tenants; Notify selects the closest locale, falling back to `en-us`. - Store connector-specific hints (`metadata.layout`, `metadata.emoji`) in template metadata rather than rules when they affect rendering. - Version template bodies through metadata (e.g., `metadata.revision: "2025-10-28"`) so tenants can track changes over time. - Run test previews whenever introducing new helpers to confirm body hashes remain stable across environments. --- > **Imposed rule reminder:** Work of this type or tasks of this type on this component must also be applied everywhere else it should be applied.