# Dynamic templating

Dynamic templating lets you customize notification content and routing using Tera expressions. Use it to make messages, routing rules, and connector configurations respond to alert or case data in real time.

## Where to use dynamic templating

Tera expressions work in:

- **Connectors**: dynamically set URLs, headers, channels, or service keys
- **Presets**: customize message content for alerts and cases
- **Routing rules**: create conditions that determine where notifications are sent

## What dynamic templating does

Dynamic templating supports:

- Inserting alert or case values directly into message templates
- Applying conditional logic (`if`, `elif`, `else`)
- Formatting timestamps, numbers, and text
- Routing notifications based on metadata such as priority, team, or service

## Global context available

All templates have access to a built-in `_context` variable containing metadata about the notification source.

| Variable                                   | Description                                                                           |
| ------------------------------------------ | ------------------------------------------------------------------------------------- |
| `_context`                                 | Contains metadata about the context of the notification source type.                  |
| `_context.entityType`                      | The notification source type (`alerts`, `cases`).                                     |
| `_context.entitySubType`                   | The entity subtype (for example, `metricThresholdTriggered`).                         |
| `_context.entityLabels`                    | Notification source type labels associated with the context (e.g., `host`, `region`). |
| `_context.system`                          | Information about the system, including its ID and name.                              |
| `_context.system.id`                       | The system's unique identifier.                                                       |
| `_context.system.name`                     | Your Coralogix team or system name (e.g., `acme-prod`).                               |
| `_context.trigger`                         | The trigger that initiated the notification source type.                              |
| `_context.trigger.type`                    | Trigger type (`manual`, `automatic`).                                                 |
| `_context.trigger.automaticTrigger`        | Automatic trigger details (if applicable).                                            |
| `_context.trigger.manualTrigger`           | Manual trigger details (e.g., user email).                                            |
| `_context.trigger.manualTrigger.userEmail` | The email address of the user who manually triggered the notification source type.    |

## Label data shapes: alerts vs. Cases

Label data has different shapes depending on its source. The same key can be a single value or an array of values, so use the right read form for each. If you read an array-shaped label without indexing into it, the template renders the array literal, including the surrounding square brackets. The bracketed output then propagates into whatever the template feeds, for example Slack channel names, HTTPS payload fields, ServiceNow ticket fields, or email subject lines.

| Variable                    | Source               | Shape              | How to read                                  |
| --------------------------- | -------------------- | ------------------ | -------------------------------------------- |
| `alertDef.entityLabels`     | Alert definition     | `{ key: value }`   | `{{ alertDef.entityLabels["Service"] }}`     |
| `alert.groups[N].keyValues` | Alert group          | `{ key: value }`   | `{{ alert.groups[0].keyValues["Service"] }}` |
| `_context.entityLabels`     | Notification context | `{ key: value[] }` | `{{ _context.entityLabels["Service"][0] }}`  |
| `case.labels`               | Case                 | `{ key: value[] }` | `{{ case.labels["Service"][0] }}`            |

For alerts, `_context.entityLabels` arrays always contain a single value. For Cases, the array can contain multiple values; use `[0]` to read the first one. For `case.labels` and `_context.entityLabels`, always index into the array. For `alertDef.entityLabels` and `alert.groups`, read the value directly.

To confirm the shape for a specific notification before writing a template, see [Inspect context](#inspect-context).

## Common use cases

### Insert dynamic values

```text
{{ alert.highestPriority }} - {{ alertDef.name }}
```

### Conditional output

```tera
{% if alert.highestPriority == "P1" %}
#critical-alerts
{% elif alert.highestPriority == "P3" %}
#warn-alerts
{% else %}
#general
{% endif %}
```

### Format dates

```text
{{ alert.timestamp | date(format="%Y-%m-%d %H:%M") }}
```

### Use default values

```text
{{ alertDef.entityLabels.team | default(value="no team assigned") }}
```

## Inspect context

To view the entire context object during configuration, use:

```text
{{ get_context() | json_encode(pretty = true) }}
```

Note

- Wrap dynamic expressions in `{{ }}` or `{% %}`.
- Use the `default` filter to handle missing values.
- Keep templates simple; avoid unnecessary complexity.
- Test templates with example data before saving.

## Example: conditional alert handling based on name

```text
{% if alertDef.name == "RDS Instance Low CPU" %}
affected db: {{ alert.groups[0].keyValues.DBInstanceIdentifier }}
credit balance is below: {{ i.condition.threshold }}
owning team: {{ alert.groups[0].keyValues.Team }}

{% elif alertDef.name == "OOM killed pod" %}
pod: {{ alert.groups[0].keyValues.k8s_pod_name }}
container: {{ alert.groups[0].keyValues.k8s_container_name }}

{% else %}
{{ alertDef.name }} is triggering
{% endif %}
```

## Example: logs threshold alert description

```text
{{ alert.timestamp | date(format="%Y-%m-%d %H:%M") }}

We've detected that the query result has dropped below the threshold for the following values of {{ alertDef.groupByKeys }}:

Priority / Values / Threshold / Start Time / End Time
{% for i in alert.groups %}
{{ i.priority }} / {{ i.keyValues | json_escape }} / {{ i.details.logsThreshold.fromTimestamp | date(format="%Y-%m-%d %H:%M") }} / {{ i.details.logsThreshold.toTimestamp }}
{% endfor %}

Alert Query:
{{ alertDef.typeDefinition.logsThreshold.luceneQuery }}

Alert Condition Rules:
Condition type - {{ alertDef.typeDefinition.logsThreshold.rules[0].condition.conditionType }}
Condition rules -
{% for i in alertDef.typeDefinition.logsThreshold.rules %}
Threshold: {{ i.condition.threshold }}, timeframe: {{ i.condition.timeWindow.logsTimeWindowSpecificValue }}.
{% endfor %}

@jane.doe  
@here
```

## Example: grouping by service

```json
{
  "alert_url": "https://teamname-prod.app.eu2.coralogix.com/#/alerts/{{ alert.id }}",
  "service": "{{ alert.groups[0].keyValues.service_name }}",
  "priority": "{{ alert.highestPriority }}",
  "timestamp": "{{ alert.timestamp | date(format=\"%Y-%m-%d %H:%M\") }}",
  "status": "{{ alert.status }}"
}
```

## Next steps

Explore operators, filters, and tests for building Tera expressions in [Tera syntax quick reference](https://coralogix.com/docs/user-guides/notification-center/dynamic-templating/tera-syntax/index.md).
