---
title: "Require incident fields on resolve | Grafana Cloud documentation"
description: "When an incident is resolved, check for required custom fields and labels, and nudge the team to fill in anything that is missing."
---

> For a curated documentation index, see [llms.txt](/llms.txt). For the complete documentation index, see [llms-full.txt](/llms-full.txt).

# Require incident fields on resolve

> Note
> 
> Grafana Workflows is currently in [public preview](https://grafana.com/docs/release-life-cycle/). 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](https://grafana.com/docs/grafana-cloud/alerting-and-irm/irm/integrations/chat-and-collaboration/slack/).

## Steps

Steps run in order from first to last. A step inside a switch branch runs only when Workflows chooses that branch.

Expand table

| 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](https://grafana.com/docs/grafana-cloud/platform/workflows/reference/step-trigger-types).

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](https://grafana.com/docs/grafana-cloud/platform/workflows/as-code).

YAML ![Copy code to clipboard](https://grafana.com/media/images/icons/icon-copy-small-2.svg) Copy

```yaml
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:

text ![Copy code to clipboard](https://grafana.com/media/images/icons/icon-copy-small-2.svg) Copy

```text
inputs.data.eventKind == 'incident_updated_status' && inputs.data.newStatus == 'resolved' && !resources.incident.event.isDrill
```

It 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](https://grafana.com/docs/grafana-cloud/platform/workflows/build/triggers/#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 `eventKind` `incident_updated_status` when `newStatus` is resolved.
- A Python `code.execute` step reads the auto-loaded incident from context\[“resources”]\[“incident”]\[“event”] and compares its `fieldValues` (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.
- `runOnceFor` keys 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) and `requiredLabelKeys` in args.

## Next steps

- [Blueprints](https://grafana.com/docs/grafana-cloud/platform/workflows/reference/blueprints)
- [Start from a blueprint](https://grafana.com/docs/grafana-cloud/platform/workflows/build/use-blueprints)
- [Manage workflows as code](https://grafana.com/docs/grafana-cloud/platform/workflows/as-code)
- [Step and trigger types reference](https://grafana.com/docs/grafana-cloud/platform/workflows/reference/step-trigger-types)
