Cases
Cases are a supported entity type in Notification Center. When Case lifecycle events occur, Notification Center generates notification requests that can be routed to external destinations.
Supported entity subtypes
An entity subtype adds a layer of granularity to an entity type. For Cases, an entity subtype consists of two elements: state and status.
| Entity | State | Status | Description |
|---|---|---|---|
| Cases | CREATED | OPEN | A new Case has been created and is open for handling. |
| Cases | ACTIVE | OPEN | The Case is actively in progress and remains open. |
| Cases | ACTIVE | ACKNOWLEDGED | The Case is being handled and has been explicitly acknowledged. |
| Cases | RESOLVED | CLOSED | The Case has been resolved and closed, with optional resolution metadata. |
All supported entity subtypes can be found in the Cases API reference.
Notification triggers
Case routing rules support the following lifecycle triggers:
| Trigger | When it fires |
|---|---|
| Activated | Impact is confirmed and the Case becomes active |
| Acknowledged | Someone takes ownership of the Case |
| Resolved | Underlying indicators are healthy and the Case is resolved |
| Closed | Follow-ups are complete and the Case is fully finished |
| Priority changed | The Case priority level is updated |
| Assignee changed | The Case is assigned or reassigned |
These triggers are configured in Case routing rules within a router. Each trigger can be routed to a different destination, allowing teams to control which stakeholders are notified at each stage of the incident lifecycle.
Routing and Ownership Tags
Cases use Ownership Tags (environment, service, and team) for routing. These are the same attributes used in Infra Explorer Ownership Tags, so labels already defined on your infrastructure carry through to notification routing.
Unlike alerts, which use the routing.<key>: <value> prefix, Cases inherit their routing labels from the Ownership Tags assigned to the underlying infrastructure.
Template variable reference
Every field a Case notification exposes to a preset template, organized by namespace. Use these paths in preset message bodies and routing rule conditions (Tera syntax).
Namespaces
A Case notification exposes three namespaces plus a few flat shortcuts:
| Namespace | What it holds | Use it for |
|---|---|---|
case.* | The incident itself — a snapshot at send time: title, status, priority, assignee, indicators, KPI breaches, Olly analysis, and all deep links. | Message content: "what is this incident?" |
caseMetadata.* | Why you are being notified right now — the lifecycle event and exactly what changed. | Reason lines, branching the template per event. |
_context.* | The notification envelope — shared by all entity types (alerts and cases): source type, routing labels, team, and trigger. | Routing conditions, cross-entity presets. |
| Top-level | Flat shortcuts: correlationKey, name, priority (mirror case.id / case.title / case.priority). | Integrations that expect a flat field, such as dedup keys. |
In short: case is the incident, caseMetadata is why you were notified now, and _context is how it reached you.
Reading arrays and the [0] rule
Arrays are not usable bare. Rendering an array directly (for example {{ case.kpiBreaches.breachedKpis }}) produces unusable output, and referencing a variable the Case does not have (for example {{ case.impactedEntities }} on a Case with no impacted entities) fails the entire render with Variable case.impactedEntities not found. Always index an array with [0], iterate it with a {% for %} loop, or serialize it with json_encode.
| Shape | Paths | How to read |
|---|---|---|
| True array | case.indicators.alerts / .genericIndicators / .prometheusAlerts, case.kpiBreaches.breachedKpis, case.impactedEntities, case.externalReferences, case.notificationDeliveries, remediationRecommendations, entityLinks | [0].field, a {% for %} loop, or json_encode |
Array-shaped map {key: [values]} | case.labels, case.groupings, _context.entityLabels, _context.grouping | ["key"][0] — without [0] the brackets leak into the message (["platform"] instead of platform) |
| Plain object (no index) | case.links, case.indicators.alerts[0].labels / .groupings | direct field access |
Safe patterns:
{# per-variable guard #}
{% for e in case.impactedEntities | default(value=[]) %}{{ e.name }}: {{ e.url }}
{% endfor %}
{# dump an array as JSON #}
{{ case.kpiBreaches.breachedKpis | json_encode() | safe }}
Autocomplete in the preset editor only reveals array-element fields after an index. case.impactedEntities. suggests nothing, while case.impactedEntities[0]. suggests kind, name, url, and so on.
case.* — the Case object
| Path | Description | Example |
|---|---|---|
case.id | Unique Case ID (UUID). | 76c411be-ff4d-4fb1-a987-5fce042deaaf |
case.title | Case title. | Test Case |
case.description | Case description. | This is the description of the Test Case |
case.status | Case status. Values: PENDING_ACTIVATION, ACTIVE, ACKNOWLEDGED, RESOLVED, CLOSED. | ACKNOWLEDGED |
case.priority | Priority P1–P5. | P4 |
case.category | Case category. | AVAILABILITY |
case.url | Deep link to the Case in Coralogix. | https://team.coralogix.com/#/cases?id=CASE-123 |
case.links.watchCase | Deep link — watch the Case. | ...?id=CASE-123 |
case.links.assignToMe | Deep link — assign to me. | ...?id=CASE-123&action=assign |
case.links.ackCase | Deep link — acknowledge. | ...?id=CASE-123&action=ack |
case.links.resolveCase | Deep link — resolve. | ...?id=CASE-123&action=resolve |
case.links.closeCase | Deep link — close. | ...?id=CASE-123&action=close |
case.assignee | Current assignee — tagged union, see Actor tagged unions. | {{ case.assignee.coralogixUser.userEmail }} |
case.createdAt | Created (ISO 8601). | 2025-08-10T09:15:00Z |
case.updatedAt | Last updated. | 2025-08-10T09:25:00Z |
case.acknowledgedAt / case.acknowledgedBy | Ack time and actor (tagged union). | {{ case.acknowledgedBy.coralogixUser.userEmail }} |
case.closedAt / case.closedBy | Close time and actor (tagged union). | 2025-08-11T09:15:00Z |
case.resolutionDetails | resolutionReason, resolvedAt, resolvedBy (tagged union). | {{ case.resolutionDetails.resolutionReason }} |
case.labels | Case labels. Array-shaped {key: [values]} — index [0]. | {{ case.labels["team"][0] }} |
case.groupings | Aggregated group-by values across indicators. Array-shaped. | {{ case.groupings["service"][0] }} |
case.indicators | The signals behind the Case — three arrays: alerts, genericIndicators, prometheusAlerts. | {{ case.indicators.alerts[0].title }} |
case.kpiBreaches.breachedKpis[] | KPI breaches: id, createdAt, kpiType (TIME_TO_ACKNOWLEDGE, TIME_TO_RESOLVE, TIME_TO_UPDATE), casePriority, thresholdMinutes, breachedAt, mitigatedAt, breachStatus. | {{ case.kpiBreaches.breachedKpis[0].breachStatus }} |
case.ollyAnalysis | Olly investigation: investigationSummary, rootCause, rootCauseConfidence (LOW / MEDIUM / HIGH), remediationRecommendations[], generatedAt. | {{ case.ollyAnalysis.rootCause }} |
case.impactedEntities[] | Impacted entities: kind, language, name, system, url (deep link to the entity). Guard with default. | {% for e in case.impactedEntities %}{{ e.name }}{% endfor %} |
case.externalReferences[] | Linked external items such as tickets: externalId, url, vendor. | {{ case.externalReferences[0].url }} |
case.notificationDeliveries[] | Notifications already delivered for this Case: label, url, vendor. | {{ case.notificationDeliveries[0].url }} |
Alert indicators — case.indicators.alerts[0].*
A Case can hold multiple indicators — always index with [0] or loop.
Path (under case.indicators.alerts[0].) | Description | Example |
|---|---|---|
instanceId | The specific alert event. | instance-id-1 |
alertDefinitionId | Stable alert-definition ID. | 987e4567-e89b-... |
alertDefinitionVersionId | Versioned definition ID. | ver12345-6789-... |
title | Alert title. | Test CPU Alert |
description | Alert description. | Triggers when average CPU usage... |
alertType | Alert type. | METRIC_THRESHOLD |
priority | Alert priority. | P4 |
state | Lifecycle state. | TRIGGERED / RESOLVED |
isNoData | No-data alert flag. | false |
groupingType | Grouping mode. | COMBINATION_ALERT |
groupByKeys | Group-by keys. | ["service", "region"] |
groupings | Group-by values — object, not array-shaped. | {"service": "payment-api"} |
labels | Alert labels — object, not array-shaped. | {"metric": "cpu", "team": "platform"} |
timeWindow | Evaluation window, in seconds. | 300 |
threshold | Configured threshold. | 80.0 |
triggeredAt / resolvedAt | Trigger and resolve times. | 2025-08-10T09:14:00Z / null |
alertQuery.queryString | The alert query. | avg(node_cpu_seconds_total{...}) > 0.8 |
alertQuery.type | Query type. | ALERT_QUERY_PROMQL |
details.fromTimestamp / .toTimestamp | Violation window. | 2025-08-10T09:09:00Z |
details.countOverThreshold | Matches over threshold. | 12 |
details.valueOverThreshold | Value that crossed the threshold. | 83.7 |
details.relativeHitCount | Ratio-alert denominator count. | null |
details.uniqueCountValuesList | Unique-count alerts. | null |
details.logExample.* | Sample log: text, applicationName, subsystemName, service, ipAddress, computerName, threadId, timestamp, category, logId. | ERROR: CPU usage at 83.7%... |
Other indicator types
| Path | Fields |
|---|---|
case.indicators.genericIndicators[0]. | id, externalId, indicatorType, status, priority, labels, lastTriggeredAt, lastResolvedAt, entityLinks[] (each: url) |
case.indicators.prometheusAlerts[0]. | alertGroupId, alerts, externalUrl, groupLabels, priority, receivedAt, receiver, status, updatedAt |
caseMetadata.* — why this notification fired
| Path | Description | Example |
|---|---|---|
caseMetadata.notificationReason | Lifecycle event that fired the notification. Values: caseActivated, caseUpdated, caseResolved, caseClosed. | caseClosed |
caseMetadata.change | What changed — a tagged union on $type. Fields across variants: $type, previousStatus / currentStatus, previousPriority / currentPriority, previousAssignee / currentAssignee, kpiType, thresholdMinutes. Only the fields matching $type are set. | {"$type": "statusChanged", "previousStatus": "ACTIVE", "currentStatus": "CLOSED"} |
Actor tagged unions
case.assignee, case.acknowledgedBy, case.closedBy, and case.resolutionDetails.resolvedBy are tagged unions: a $type discriminator plus one inner object. Check $type before reading inner fields.
$type | Fields |
|---|---|
coralogixUser | userEmail |
serviceNowUser | userEmail |
apiKey | keyName |
slackUser | coralogixUserEmail, slackUserEmail, displayName |
pagerDutyUser | coralogixUserEmail, pagerDutyUserEmail, displayName |
microsoftTeamsUser | coralogixUserEmail, teamsUserEmail, displayName |
prometheusAlertManager | receiverName |
system | (empty — automated action) |
_context.* — the notification envelope
| Path | Description | Example |
|---|---|---|
_context.entityType | Source type. | cases |
_context.entitySubType | Subtype. Empty in the sample; preset overrides match on caseActivated, caseUpdated, caseResolved. | "" |
_context.entityLabels | Routing and ownership labels. Array-shaped — index [0]. | {{ _context.entityLabels["environment"][0] }} |
_context.grouping | Grouping values in the notification context. Array-shaped. | {"service": ["alerts", "notifications"]} |
_context.system | Coralogix team: id, name. | {{ _context.system.name }} |
_context.trigger | type (manual / automatic), manualTrigger.userEmail, automaticTrigger. | {{ _context.trigger.type }} |
Top-level variables and functions
| Name | Description |
|---|---|
correlationKey | Stable per-Case key (mirrors case.id) — use as a dedup key for PagerDuty or ServiceNow-style integrations. |
name | Mirrors case.title. |
priority | Mirrors case.priority. |
get_context() | The whole context object. Useful for debugging — see the tip below. |
now(), get_random(), range() | Standard Tera functions. |
Full example payload
The complete shape a Case notification passes to a template, as rendered by get_context() in the preview:
{
"_context": {
"entityType": "cases",
"entitySubType": "",
"entityLabels": { "environment": ["production"] },
"grouping": { "service": ["alerts", "notifications"] },
"trigger": {
"type": "manual",
"manualTrigger": { "userEmail": "user@email.com" },
"automaticTrigger": null
},
"system": { "id": "1234567", "name": "team-name" }
},
"case": {
"id": "76c411be-ff4d-4fb1-a987-5fce042deaaf",
"title": "Test Case",
"url": "https://team.coralogix.com/#/cases?id=CASE-123",
"links": {
"watchCase": "https://team.coralogix.com/#/cases?id=CASE-123",
"assignToMe": "https://team.coralogix.com/#/cases?id=CASE-123&action=assign",
"ackCase": "https://team.coralogix.com/#/cases?id=CASE-123&action=ack",
"resolveCase": "https://team.coralogix.com/#/cases?id=CASE-123&action=resolve",
"closeCase": "https://team.coralogix.com/#/cases?id=CASE-123&action=close"
},
"description": "This is the description of the Test Case",
"assignee": { "$type": "coralogixUser", "coralogixUser": { "userEmail": "test-user@coralogix.com" } },
"status": "ACKNOWLEDGED",
"priority": "P4",
"category": "AVAILABILITY",
"createdAt": "2025-08-10T09:15:00Z",
"updatedAt": "2025-08-10T09:25:00Z",
"acknowledgedAt": "2025-08-10T09:16:30Z",
"acknowledgedBy": { "$type": "coralogixUser", "coralogixUser": { "userEmail": "test-user@coralogix.com" } },
"resolutionDetails": {
"resolutionReason": "This was a false alert.",
"resolvedAt": "2025-08-11T09:15:00Z",
"resolvedBy": { "$type": "system", "system": {} }
},
"closedAt": "2025-08-11T09:15:00Z",
"closedBy": { "$type": "coralogixUser", "coralogixUser": { "userEmail": "test-user@coralogix.com" } },
"indicators": {
"alerts": [
{
"instanceId": "instance-id-1",
"alertDefinitionId": "987e4567-e89b-12d3-a456-426614174111",
"alertDefinitionVersionId": "ver12345-6789-abcd-ef01-234567890abc",
"title": "Test CPU Alert",
"description": "Triggers when average CPU usage on payment-api exceeds 80% over a 5-minute window.",
"alertType": "METRIC_THRESHOLD",
"priority": "P4",
"groupingType": "COMBINATION_ALERT",
"groupByKeys": ["service", "region"],
"timeWindow": 300,
"threshold": 80.0,
"groupings": { "service": "payment-api", "region": "us-west-2" },
"labels": { "metric": "cpu", "team": "platform" },
"state": "TRIGGERED",
"isNoData": false,
"triggeredAt": "2025-08-10T09:14:00Z",
"resolvedAt": null,
"alertQuery": {
"queryString": "avg(node_cpu_seconds_total{mode=\"system\",service=\"payment-api\"}) > 0.8",
"type": "ALERT_QUERY_PROMQL"
},
"details": {
"fromTimestamp": "2025-08-10T09:09:00Z",
"toTimestamp": "2025-08-10T09:14:00Z",
"countOverThreshold": 12,
"relativeHitCount": null,
"valueOverThreshold": 83.7,
"uniqueCountValuesList": null,
"logExample": {
"text": "ERROR: CPU usage at 83.7% on payment-api pod prod-worker-03",
"applicationName": "payment-service",
"subsystemName": "checkout",
"service": "payment-api",
"ipAddress": "10.0.1.21",
"computerName": "prod-worker-03",
"threadId": "main-thread",
"timestamp": "2025-08-10T09:13:45Z",
"category": "ERROR",
"logId": "log-def456abc789"
}
}
}
],
"genericIndicators": [
{
"id": "gi-1", "externalId": "ext-1", "indicatorType": "custom", "status": "TRIGGERED",
"priority": "P4", "labels": {}, "lastTriggeredAt": "2025-08-10T09:14:00Z",
"lastResolvedAt": null, "entityLinks": [ { "url": "https://team.coralogix.com/#/..." } ]
}
],
"prometheusAlerts": [
{
"alertGroupId": "pg-1", "alerts": [], "externalUrl": "https://alertmanager/...",
"groupLabels": {}, "priority": "P4", "receivedAt": "2025-08-10T09:14:00Z",
"receiver": "team-receiver", "status": "firing", "updatedAt": "2025-08-10T09:20:00Z"
}
]
},
"groupings": { "service": ["payment-api"], "region": ["us-west-2"] },
"labels": { "metric": ["cpu"], "team": ["platform"] },
"kpiBreaches": {
"breachedKpis": [
{ "id": "kpi-breach-1", "createdAt": "2025-08-10T09:20:00Z", "kpiType": "TIME_TO_ACKNOWLEDGE", "casePriority": "P4", "thresholdMinutes": 1, "breachedAt": "2025-08-10T09:20:00Z", "mitigatedAt": "2025-08-10T09:22:30Z", "breachStatus": "MITIGATED" }
]
},
"ollyAnalysis": {
"investigationSummary": "Olly correlated the Test CPU Alert with a deploy at 09:10 and observed sustained CPU saturation on payment-api.",
"rootCause": "Regression in payment-api v42.1 increased per-request allocations and pushed CPU above the threshold.",
"rootCauseConfidence": "MEDIUM",
"remediationRecommendations": [
"Roll back payment-api to v42.0.",
"Profile the request handler in v42.1 to locate the allocation regression."
],
"generatedAt": "2025-08-10T09:23:00Z"
},
"impactedEntities": [
{ "kind": "service", "language": "go", "name": "payment-api", "system": "apm", "url": "https://team.coralogix.com/#/apm/services/payment-api" }
],
"externalReferences": [
{ "externalId": "JIRA-123", "url": "https://example.atlassian.net/browse/JIRA-123", "vendor": "jira" }
],
"notificationDeliveries": [
{ "label": "#alerts-prod", "url": "https://example.slack.com/archives/C123/p456", "vendor": "slack" }
]
},
"caseMetadata": {
"notificationReason": "caseClosed",
"change": { "$type": "statusChanged", "previousStatus": "ACTIVE", "currentStatus": "CLOSED" }
},
"correlationKey": "76c411be-ff4d-4fb1-a987-5fce042deaaf",
"name": "Test Case",
"priority": "P4"
}
Gotchas
- Array-shaped labels:
case.labels,case.groupings,_context.entityLabels, and_context.groupingare{key: [values]}— read them with[0]. Butcase.indicators.alerts[0].labels/.groupingsare plain objects ({key: value}) — no index. - Arrays need
[0]: indicators,kpiBreaches,impactedEntities,externalReferences, andnotificationDeliveries. Editor autocomplete only reveals element fields after an index. - Objects in JSON payloads: serialize with
json_encode() | safe(addtruncate(length=2990)for Slack's 3,000-character block limit). - Tagged unions (
case.assigneeand friends,caseMetadata.change): check$typebefore reading inner fields. - Possibly-absent fields: guard
impactedEntities,externalReferences,notificationDeliveries,kpiBreaches.breachedKpis, andollyAnalysiswithdefault(...)or{% if %}.
To inspect the full payload for a specific Case, drop {{ get_context() | json_encode(pretty=true) }} into a draft preset body and read the preview.
Context reference
All templates also have access to the _context variable, which contains metadata about the notification source type, including system identifiers and trigger details. See Dynamic templating for details.
Migrating from legacy webhooks
If you are moving a legacy outbound webhook to a custom Case preset, see the webhook field mapping for the placeholder-to-template-path mapping. System presets already produce correct payloads, so this is only relevant for custom migrations.