Skip to Content

Message content

Content belongs to a (rule, destination) pair. The same Slack channel can receive different wording from two different rules, and one rule can send prose to Slack and JSON to a webhook at the same time.

Each destination a rule delivers to gets a row under Content. Click Edit message on a Slack or Microsoft Teams row, or Edit body on a webhook row.

The Content section of a rule with one row per destination, each showing whether it sends the default payload or default content, and a button to edit it

Chat and webhook destinations

DestinationBodyLeft unedited
Slack, Microsoft TeamsPlain text, rendered from your templateBuilt-in message, with separate wording for triggered and resolved
WebhookJSON, rendered from your templateThe canonical cq.notification.v1 payload

Both use Liquid for interpolation, conditionals, and loops.

You can loop over resources and over a resource’s apps, nested up to three levels.

Variable reference

Insight

VariableValue
insight.titleHuman-readable insight title
insight.severitySeverity of the insight
insight.categoryInsight category
insight.sourceWhere the insight came from
insight.urlLink back to the insight
insight.resource_countTotal live violations for the insight
insight.idInsight identifier

Rule and event

VariableValue
rule.nameRule name
rule.idRule identifier
event.statustriggered or resolved
event.cycle_atWhen the evaluation cycle ran, RFC 3339 in UTC
event.idDelivery identifier, stable across retries

Resources

resources is the list of resources this notification is about. resource is an alias for the first entry.

VariableValue
resource.idResource identifier
resource.nameResource name, falling back to the identifier
resource.typeResource type, for example aws_s3_bucket
resource.accountCloud account
resource.regionCloud region
resource.cloudCloud provider
resource.ownerResolved owner, or null
resource.tagsTag map — index by key, for example resource.tags.env
resource.environmentsList of environment names
resource.appsList of apps, each with id and name

Policy

Available on insights a policy produced. On any other insight policy is null, so every reference needs a guard:

{% if policy %}{{ policy.name }}{% endif %}
VariableValue
policy.namePolicy name
policy.severityPolicy severity
policy.domainPolicy domain, for example security
policy.descriptionPolicy description, empty when unset
policy.urlLink back to the policy
policy.idPolicy identifier
policy.query_nameName of the saved query behind the policy
policy.query_urlLink to that query in the SQL Console

The variable picker in the editor inserts the guard along with the variable.

Envelope

Additional variables that carry information about the notification.

VariableValue
schema_versionPayload contract version, cq.notification.v1
_resource_totalThis notification’s batch size, before the cap
_resources_omittedHow many resources the cap left out
truncatedTrue when the list was trimmed, by count or by size

Emitting JSON with the json filter

In a webhook body, interpolation is escaped so a resource name carrying a quote cannot break your JSON. That also means {{ resources }} alone emits an escaped string. Use the json filter for real structures:

{ "text": "{{ insight.title }} on {{ resource.name }}", "resources": {{ resources | json }}, "tags": {{ resource.tags | json }} }

Guard what can be absent

A reference to a variable that does not exist fails the render, and a failed render fails that one delivery.

resource is omitted, not empty, when resources has no entries — so an unguarded {{ resource.name }} fails the render rather than printing nothing:

{% if resources.size > 0 %}{{ resource.name }}{% else %}All clear{% endif %}

A resolved notification does carry its resources but they arrive with only their identifier. name, account, region and the rest are empty, and name falls back to the identifier. Write templates that read well either way.

resource.owner is null on a resource with no ownership row. Liquid renders null as empty, so guard it where an empty value would read badly:

Owner: {{ resource.owner | default: "unassigned" }}

One template, both statuses

A rule’s content serves the triggered and the resolved notification. Branch on event.status where the wording has to differ:

{% if event.status == "resolved" %} Cleared: {{ insight.title }} no longer affects {{ rule.name }} resources. {% else %} {{ insight.title }}{{ _resource_total }} newly affected resources. {% endif %}

Preview

Click Preview in the editor to render your template. The result appears under Rendered output, beside the body. The preview runs the same code path as the save-time validation, so an error here is the error a save would report, with the line and column.

The content editor with a Liquid template on the left and its rendered output on the right, alongside the Insert variable and Preview buttons

The insight and policy fields render their real values; the resources are examples, so the names you see are not the resources this rule would claim. Use the match preview for that.

The preview renders the triggered status. Read a {% if event.status == "resolved" %} branch by inverting the condition while you check it.

Previews need an insight behind the list. The organization default list offers none.

Limits

LimitValue
Template size32 KB
Rendered output256 KB
Render time500 ms
Resources in a webhook body50
Resources in a chat message10

If there are too many resources or the total size of the notification body reaches the size limit, the resource lists are truncated. Use the Envelope variables to detect the truncation.

Liquid language limitations

CloudQuery Platform does not support the following:

  • ranges and range loops, such as {% for i in (1..9) %}
  • {% tablerow %} and {% capture %} blocks
  • {% include %} and {% render %} tags

Legacy placeholders

Webhook bodies written before notification rules used flat placeholders on the destination. They keep working in rule content, so a migrated body renders unchanged:

insight_title, insight_category, insight_source, insight_resource_count, insight_url, alert_severity, alert_status, alert_violations, alert_message, query_name, query_url

alert_violations is this notification’s batch size; insight_resource_count is the insight’s total live violations. query_name and query_url are empty on a built-in insight, which has no saved query behind it.

Write new templates against the object graph above. The flat names exist for compatibility.

Next steps

Was this page helpful?

Last updated on