Documentation for automated readers
A curated documentation index is available at: https://grafana.com/llms.txt
A complete documentation index is available at: https://grafana.com/llms-full.txt
These indexes can help with page discovery before fetching individual documents.
This page is also available in Markdown, which may be easier for automated readers and AI tools to parse than HTML. The Markdown version is available at https://grafana.com/docs/grafana-cloud/platform/workflows/reference/blueprints/require-incident-fields-on-resolve.md, or by sending Accept: text/markdown to https://grafana.com/docs/grafana-cloud/platform/workflows/reference/blueprints/require-incident-fields-on-resolve/. For broader documentation discovery, the curated index is available at https://grafana.com/llms.txt and the complete index is available at https://grafana.com/llms-full.txt.
Require incident fields on resolve
Note
Grafana Workflows is currently in public preview. Grafana Labs offers limited support, and breaking changes might occur prior to the feature being made generally available.
When an incident is resolved, check for required custom fields and labels, and nudge the team to fill in anything that is missing.
This Grafana Workflows blueprint is in the Incidents category of the Blueprints library.
Before you begin
To use this blueprint, you need:
- Grafana IRM set up on your stack.
- Slack connected to Grafana IRM. For details, refer to Configure Slack for Grafana IRM.
Steps
Steps run in order from first to last. A step inside a switch branch runs only when Workflows chooses that branch.
| Step | ID | Type | Description |
|---|---|---|---|
| Find missing fields and labels | find-missing | code.execute | Checks the incident for required custom fields and label keys. Edit args to list the custom field slugs (from IRM settings) and label keys your process requires. |
| Anything missing? | anything-missing | filter | Stops the run when the incident already has everything. No changes needed. |
| List missing items on the timeline | note-missing | incident.add-note | Adds a timeline note listing what is missing. Edit the wording to match your process. |
| Has an incident channel? | has-slack-channel | filter | Stops here when the incident has no linked Slack channel. No changes needed. |
| Nudge the incident channel | nudge-channel | slack.message.post | Asks the team in the incident channel to fill in the missing items. No changes needed. |
For the inputs and outputs of each step type, refer to Step and trigger types reference.
Before you turn on the workflow, customize any steps that need input from you, such as a channel ID or a schedule name. Each step’s description says what to set.
Workflow definition
Save this definition as a YAML file to create the workflow with gcx, the Workflows API, or Terraform, or give it to a coding agent as an example.
Replace FOLDER_UID with the UID of the folder for the workflow.
Blueprints start disabled, so finish customizing the steps before you set enabled to true.
For details, refer to Manage workflows as code.
apiVersion: workflows.ext.grafana.app/v1alpha1
kind: Definition
metadata:
name: require-incident-fields-on-resolve
annotations:
grafana.app/folder: <FOLDER_UID>
spec:
name: Require incident fields on resolve
enabled: false
runOnceFor: 'required-fields:${inputs.data.incidentID}'
startWhen:
matchingRules:
- eventNameRegex: 'grafana_irm_app\.incident\.updated'
filter: "inputs.data.eventKind == 'incident_updated_status' && inputs.data.newStatus == 'resolved' && !resources.incident.event.isDrill"
schedules: []
steps:
- id: find-missing
type: code.execute
name: Find missing fields and labels
description: >-
Checks the incident for required custom fields and label keys. Edit args to list the
custom field slugs (from IRM settings) and label keys your process requires.
inputs:
language: 'python'
args: '{"requiredFields": ["root_cause", "customer_impact"], "requiredLabelKeys": ["service", "team"]}'
code: |
import json
incident = context["resources"]["incident"]["event"]
field_values = incident.get("fieldValues") or {}
label_keys = {label.get("key") for label in (incident.get("labels") or [])}
missing = []
for slug in args.get("requiredFields", []):
if field_values.get(slug) in (None, "", []):
missing.append(f"custom field `{slug}`")
for key in args.get("requiredLabelKeys", []):
if key not in label_keys:
missing.append(f"label `{key}`")
print(json.dumps({
"missing": missing,
"bullets": "\n".join(f"• {item}" for item in missing),
}))
- id: anything-missing
type: filter
name: Anything missing?
description: Stops the run when the incident already has everything. No changes needed.
inputs:
condition: "size(json_parse(steps['find-missing'].outputs.stdout).missing) > 0"
- id: note-missing
type: incident.add-note
name: List missing items on the timeline
description: Adds a timeline note listing what is missing. Edit the wording to match your process.
inputs:
incidentID: '${inputs.data.incidentID}'
message: |
This incident was resolved without some required information. Please add:
${json_parse(steps['find-missing'].outputs.stdout).bullets}
- id: has-slack-channel
type: filter
name: Has an incident channel?
description: Stops here when the incident has no linked Slack channel. No changes needed.
inputs:
condition: "has(resources.incident.event.slackChannelID)"
- id: nudge-channel
type: slack.message.post
name: Nudge the incident channel
description: Asks the team in the incident channel to fill in the missing items. No changes needed.
inputs:
channelID: '${resources.incident.event.slackChannelID}'
messageText: |
:memo: Thanks for resolving this incident. Before you move on, please add these on the <${resources.incident.event.overviewURL}|incident page>:
${json_parse(steps['find-missing'].outputs.stdout).bullets}How it works
This section explains when the workflow runs and how its steps work together.
When it runs
This blueprint starts on the grafana_irm_app.incident.updated event.
Its trigger filter starts a run only when this CEL expression is true:
inputs.data.eventKind == 'incident_updated_status' && inputs.data.newStatus == 'resolved' && !resources.incident.event.isDrillIt runs at most once for each run-once key. This blueprint’s key is required-fields:${inputs.data.incidentID}, so a later event with the same key doesn’t start another run. For details, refer to Deduplicate workflow runs.
How the steps fit together
These notes cover how the steps connect and which Workflows behaviors the blueprint relies on.
- Triggers on
eventKindincident_updated_statuswhennewStatusis resolved. - A Python
code.executestep reads the auto-loaded incident from context[“resources”][“incident”][“event”] and compares itsfieldValues(custom fields keyed by slug) and labels (objects with key and label) against the required lists passed in args. - args is a JSON string, deserialized into a dict named args.
- The step prints a JSON object to stdout.
- Later steps parse it with
json_parse(steps['find-missing'].outputs.stdout). - A filter stops the run when nothing is missing.
- The note always posts, and the Slack nudge posts only when the incident has a linked channel.
runOnceForkeys on the incident ID, so the team gets one nudge per incident.- This also handles IRM sending two status events for one resolution.
- Customize
requiredFields(custom field slugs) andrequiredLabelKeysin args.
Next steps
Was this page helpful?
Related resources from Grafana Labs


