---
title: "Step and trigger types reference | Grafana Cloud documentation"
description: "Reference documentation for all available Grafana Workflows step types and trigger types, including inputs, outputs, and usage details."
---

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

# Step and trigger types reference

> Note
> 
> Grafana Workflows is currently in [public preview](/docs/release-life-cycle/). Grafana Labs offers limited support, and breaking changes might occur prior to the feature being made generally available.

This reference lists the step types and trigger types you can use in Grafana Workflows, with their inputs, outputs, and configuration.

## Step types

Steps are the individual actions in a workflow. They execute in order from first to last. Each step has a type, an ID, and a set of inputs. Every step produces outputs that later steps can reference through the workflow context using CEL expressions, for example, `${steps['my-step'].outputs.responseBody}`. Step IDs that contain a hyphen need bracket notation, as in this example, because CEL reads a hyphen after a dot as subtraction.

For details on connecting steps with the workflow context, refer to [Use CEL expressions](/docs/grafana-cloud/platform/workflows/use-expressions).

### General

General steps make HTTP calls and run code.

#### HTTP call (`http.call`)

Send an HTTP request to a public URL and return the response status code, body, and headers. Supports bearer token and basic authentication using secrets.

The step only calls public addresses. Requests to private or internal network addresses are blocked. Each request times out after 30 seconds and follows up to 5 redirects. The request body can be up to 1 MiB. A response body longer than 1 MiB is truncated to 1 MiB.

A 4xx or 5xx response doesn’t fail the step. To act on an error response, check `responseCode` in a later step, for example, in a filter step.

**Inputs:**

Expand table

| Name           | Type           | Required | Description                                                                                                               |
|----------------|----------------|----------|---------------------------------------------------------------------------------------------------------------------------|
| `method`       | string         | Yes      | HTTP method: `GET`, `POST`, `PUT`, `PATCH`, `DELETE`, `HEAD`, or `OPTIONS`.                                               |
| `url`          | string         | Yes      | The request URL.                                                                                                          |
| `body`         | string         | No       | The request body.                                                                                                         |
| `headers`      | array (string) | No       | Request headers as a JSON array of `"Key: Value"` strings, for example, `["Accept: application/json"]`.                   |
| `authMethod`   | string         | No       | Authentication method: `bearer` or `basic`.                                                                               |
| `authSecret`   | `secretString` | No       | Secret for bearer token authentication. Refer to [Manage secrets](/docs/grafana-cloud/platform/workflows/manage-secrets). |
| `authUsername` | `secretString` | No       | Username for basic authentication. Refer to [Manage secrets](/docs/grafana-cloud/platform/workflows/manage-secrets).      |
| `authPassword` | `secretString` | No       | Password for basic authentication. Refer to [Manage secrets](/docs/grafana-cloud/platform/workflows/manage-secrets).      |

**Outputs:**

Expand table

| Name              | Type           | Description                    |
|-------------------|----------------|--------------------------------|
| `responseCode`    | int            | The HTTP response status code. |
| `responseBody`    | string         | The response body.             |
| `responseHeaders` | array (string) | The response headers.          |

#### Execute code (`code.execute`)

Run sandboxed code inside a workflow. Your code reads the workflow context from the `context` variable and the named arguments from the `args` variable. Code runs in an isolated sandbox with no network access and a temporary filesystem.

Currently supported languages: Python.

**Inputs:**

Expand table

| Name       | Type   | Required | Description                                                                                                                                                                                                                                                                   |
|------------|--------|----------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `language` | string | Yes      | Programming language to execute. Currently supported: `python`.                                                                                                                                                                                                               |
| `code`     | string | Yes      | Source code to execute in the sandbox. Read the workflow context with `context.get("<VARIABLE>")` and the named arguments with `args.get("<NAME>")`.                                                                                                                          |
| `args`     | object | No       | Named arguments as a JSON object, available in your code as the `args` variable (a `dict` in Python, empty when not set). Use `${...}` expressions to pass workflow data and wrap each one in quotes to receive a string, for example, `{"id": "${inputs.data.incidentID}"}`. |

**Outputs:**

Expand table

| Name      | Type   | Description                                    |
|-----------|--------|------------------------------------------------|
| `stdout`  | string | Standard output captured from execution.       |
| `stderr`  | string | Standard error output captured from execution. |
| `success` | bool   | Whether execution completed without error.     |
| `error`   | string | Error message if execution failed.             |

**Injected variables:**

Two variables are automatically available inside the sandbox:

Expand table

| Variable  | Type | Description                                                                                                                                                                    |
|-----------|------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `context` | dict | The full workflow context, including trigger inputs, previous step outputs, and resolved resources. Mirrors the data available to CEL expressions. Secrets are never included. |
| `args`    | dict | The evaluated JSON object from the step’s `args` parameter. Empty dict (`{}`) when `args` isn’t set.                                                                           |

The `context` variable has the following structure:

JSON ![Copy code to clipboard](/media/images/icons/icon-copy-small-2.svg) Copy

```json
{
  "inputs": { },
  "steps": {
    "step-id": {
      "outputs": { }
    }
  },
  "resources": {
    "incident": { },
    "alertgroup": { }
  }
}
```

- `context["inputs"]` contains the trigger event data or manual test input.
- `context["steps"]` contains outputs from all previously executed steps, keyed by step ID.
- `context["resources"]` contains loaded resource data (incidents, alert groups).

**Example: process data from a previous step**

This example shows a workflow where a code step reads the response from an earlier HTTP call, processes it, and outputs structured JSON for a downstream filter step:

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

```yaml
steps:
  - id: fetch-status
    type: http.call
    name: "Get service status"
    inputs:
      method: "GET"
      url: "https://api.example.com/status"

  - id: analyze
    type: code.execute
    name: "Analyze status response"
    inputs:
      language: python
      code: |
        import json

        response = context["steps"]["fetch-status"]["outputs"]["responseBody"]
        data = json.loads(response)

        unhealthy = [s for s in data.get("services", []) if s["status"] != "healthy"]
        threshold = args.get("threshold", 1)

        print(json.dumps({
            "unhealthy_count": len(unhealthy),
            "unhealthy_services": [s["name"] for s in unhealthy],
            "should_alert": len(unhealthy) >= threshold,
        }))
      args: '{"threshold": 1}'

  - id: check-alert
    type: filter
    name: "Alert if threshold exceeded"
    inputs:
      condition: "json_parse(steps['analyze'].outputs.stdout).should_alert == true"
```

**Example: scheduled check**

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

```yaml
steps:
  - id: run-check
    type: code.execute
    inputs:
      language: python
      code: |
        has_inputs = len(context.get("inputs", {})) > 0
        print(f"Scheduled run, has trigger inputs: {has_inputs}")
```

Later steps reference code output using CEL expressions, for example, `${steps['analyze'].outputs.stdout}`.

### Flow control

Flow control steps transform data, stop runs, branch, and pause.

#### Transform (`transform`)

Evaluate a template string with embedded CEL expressions to generate dynamic content. Use `${expression}` syntax to embed expressions that reference workflow inputs and previous step outputs.

**Inputs:**

Expand table

| Name       | Type   | Required | Description                                                   |
|------------|--------|----------|---------------------------------------------------------------|
| `template` | string | Yes      | A template string with embedded `${expression}` placeholders. |

**Outputs:**

Expand table

| Name     | Type   | Description                   |
|----------|--------|-------------------------------|
| `result` | string | The rendered template string. |

#### Filter (`filter`)

Gate workflow execution on a CEL condition. If the condition evaluates to `false`, the workflow stops and no further steps execute.

**Inputs:**

Expand table

| Name         | Type   | Required | Description                                                                              |
|--------------|--------|----------|------------------------------------------------------------------------------------------|
| `condition`  | string | Yes      | A CEL expression that evaluates to a boolean.                                            |
| `failOnExit` | bool   | No       | When `true`, the workflow fails instead of exiting silently when the condition is false. |

**Outputs:**

Expand table

| Name      | Type | Description                                         |
|-----------|------|-----------------------------------------------------|
| `matched` | bool | `true` if the condition matched, `false` otherwise. |

#### Switch (`switch`)

Conditionally execute different sets of steps based on CEL expressions. Each branch has a condition and its own list of steps. The first branch whose condition evaluates to `true` runs. Include a default branch with no condition as a fallback.

**Branches:**

A `switch` step uses a `branches` array instead of `inputs`. Each branch has a `condition` (a CEL expression) and a `steps` list. Omit the `condition` to mark a branch as the default fallback.

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

```yaml
- id: route-by-severity
  type: switch
  branches:
    - condition: "resources.incident.event.severity.matches('(?i)^critical$')"
      steps:
        - id: page-oncall
          type: irm.page-team
          inputs:
            teamIdentifier: "platform"
            identifierType: "name"
    - steps:
        - id: post-update
          type: slack.message.post
          inputs:
            channelID: "C0123456789"
            messageText: "Non-critical incident received"
```

**Outputs:**

Expand table

| Name                 | Type   | Description                                                                             |
|----------------------|--------|-----------------------------------------------------------------------------------------|
| `matchedCase`        | string | The matched condition string, `default` for the default branch, or empty when no match. |
| `matchedBranchIndex` | int    | The zero-based index of the matched branch. Returns `-1` when no branch matched.        |
| `error`              | string | An error message if evaluation failed. Empty on success.                                |

#### Wait (`wait`)

Pause workflow execution for a specified number of seconds.

**Inputs:**

Expand table

| Name      | Type | Required | Description                                               |
|-----------|------|----------|-----------------------------------------------------------|
| `seconds` | int  | Yes      | The number of seconds to wait, from 0 to 604800 (7 days). |

**Outputs:**

Expand table

| Name            | Type | Description                            |
|-----------------|------|----------------------------------------|
| `waitedSeconds` | int  | The number of seconds the step waited. |

### Incident

Incident steps operate on incidents in Grafana IRM. You can pass incident IDs directly or reference them from incident resources loaded into the workflow context. For details on loading incident data, refer to [Load incident data](/docs/grafana-cloud/platform/workflows/load-incident-data).

#### Create incident (`incident.create`)

Create a new IRM incident with a title, severity, and optional labels.

**Inputs:**

Expand table

| Name       | Type           | Required | Description                                                                 |
|------------|----------------|----------|-----------------------------------------------------------------------------|
| `title`    | string         | Yes      | Title of the incident.                                                      |
| `status`   | string         | No       | Incident status: `active` or `resolved`.                                    |
| `severity` | string         | Yes      | Severity of the incident: `pending`, `critical`, `major`, or `minor`.       |
| `labels`   | array (object) | No       | Labels to attach to the incident. Each label has a `key` and `label` field. |

**Outputs:**

Expand table

| Name          | Type   | Description                                                   |
|---------------|--------|---------------------------------------------------------------|
| `success`     | bool   | `true` if the incident was created successfully.              |
| `incidentID`  | string | The identifier of the created incident.                       |
| `incidentURL` | string | The full URL of the incident overview page, for use in links. |
| `error`       | string | An error message if the operation failed.                     |

#### Add participant to incident (`incident.add-participant`)

Add a user to an IRM incident with a specific role.

**Inputs:**

Expand table

| Name         | Type   | Required | Description                                                                                                                                                             |
|--------------|--------|----------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `incidentID` | string | Yes      | The incident ID.                                                                                                                                                        |
| `userID`     | string | Yes      | Email address of the participant.                                                                                                                                       |
| `role`       | string | No       | The participant role. Built-in roles are `commander`, `investigator`, and `observer`. Custom roles configured for your stack are also accepted. Defaults to `observer`. |

**Outputs:**

Expand table

| Name         | Type   | Description                                       |
|--------------|--------|---------------------------------------------------|
| `success`    | bool   | `true` if the participant was added successfully. |
| `didChange`  | bool   | `true` if the participant was newly added.        |
| `incidentID` | string | The incident ID.                                  |
| `error`      | string | An error message if the operation failed.         |

#### Set incident field (`incident.set-field`)

Update a field on an IRM incident, including title, status, severity, or role assignments.

**Inputs:**

Expand table

| Name         | Type   | Required | Description                                                        |
|--------------|--------|----------|--------------------------------------------------------------------|
| `incidentID` | string | Yes      | The incident ID.                                                   |
| `field`      | string | Yes      | The field to update. One of `title`, `status`, `severity`, `role`. |
| `value`      | string | Yes      | The new value for the field.                                       |
| `userID`     | string | No       | Required when `field` is `role`. Email address of the participant. |

**Outputs:**

Expand table

| Name         | Type   | Description                                   |
|--------------|--------|-----------------------------------------------|
| `success`    | bool   | `true` if the field was updated successfully. |
| `incidentID` | string | The incident ID.                              |
| `error`      | string | An error message if the operation failed.     |

#### Create incident Slack channel (`incident.slack-channel.create`)

Create a Slack channel for an IRM incident.

**Inputs:**

Expand table

| Name          | Type   | Required | Description                                                                                  |
|---------------|--------|----------|----------------------------------------------------------------------------------------------|
| `incidentID`  | string | Yes      | The incident ID.                                                                             |
| `channelName` | string | Yes      | Desired Slack channel name. Workflows adjusts the name to follow Slack channel naming rules. |
| `postUpdates` | bool   | No       | Set to `true` to post incident activity updates to the created channel.                      |
| `inviteUsers` | bool   | No       | Set to `true` to invite users with active incident roles to the created channel.             |

**Outputs:**

Expand table

| Name          | Type   | Description                                     |
|---------------|--------|-------------------------------------------------|
| `success`     | bool   | `true` if the channel was created successfully. |
| `channelID`   | string | The created Slack channel ID.                   |
| `channelName` | string | The created Slack channel name returned by IRM. |
| `error`       | string | An error message if the operation failed.       |

#### Add note to incident (`incident.add-note`)

Post a note to an IRM incident’s timeline. Use this for routine progress notes. Use `incident.add-key-update` for significant milestones.

**Inputs:**

Expand table

| Name         | Type   | Required | Description                                         |
|--------------|--------|----------|-----------------------------------------------------|
| `incidentID` | string | Yes      | The incident ID to update, for example, `INC-123`.  |
| `message`    | string | Yes      | Note content to post. Supports Markdown formatting. |

**Outputs:**

Expand table

| Name             | Type   | Description                                  |
|------------------|--------|----------------------------------------------|
| `success`        | bool   | `true` if the note was posted successfully.  |
| `activityItemID` | string | The identifier of the created activity item. |
| `incidentID`     | string | The incident ID.                             |
| `error`          | string | An error message if the operation failed.    |

#### Add key update to incident (`incident.add-key-update`)

Post a key update (a significant milestone) to an IRM incident. Key updates appear as headline entries on the incident timeline and stakeholder-facing views.

**Inputs:**

Expand table

| Name         | Type   | Required | Description                                                                                           |
|--------------|--------|----------|-------------------------------------------------------------------------------------------------------|
| `incidentID` | string | Yes      | The incident ID to update, for example, `INC-123`.                                                    |
| `message`    | string | Yes      | Update content to post.                                                                               |
| `title`      | string | No       | Short summary of the update.                                                                          |
| `scope`      | string | No       | Audience or visibility of the key update: `internal`, `public`, or `private`. Defaults to `internal`. |

**Outputs:**

Expand table

| Name          | Type   | Description                                       |
|---------------|--------|---------------------------------------------------|
| `success`     | bool   | `true` if the key update was posted successfully. |
| `keyUpdateID` | string | The identifier of the created key update.         |
| `incidentID`  | string | The incident ID.                                  |
| `error`       | string | An error message if the operation failed.         |

### Slack

Slack steps manage channels and send messages in your connected Slack workspace.

#### Create Slack channel (`slack.channel.create`)

Create a new public or private Slack channel.

**Inputs:**

Expand table

| Name          | Type   | Required | Description                                |
|---------------|--------|----------|--------------------------------------------|
| `channelName` | string | Yes      | The name for the new channel.              |
| `isPrivate`   | bool   | No       | Set to `true` to create a private channel. |

**Outputs:**

Expand table

| Name          | Type   | Description                               |
|---------------|--------|-------------------------------------------|
| `channelID`   | string | The Slack channel ID.                     |
| `channelName` | string | The final channel name assigned by Slack. |

#### Post message to Slack channel (`slack.message.post`)

Post a message to a Slack channel. Supports plain text, Block Kit blocks for rich formatting, attachments, and threaded replies.

**Inputs:**

Expand table

| Name             | Type   | Required | Description                                                                    |
|------------------|--------|----------|--------------------------------------------------------------------------------|
| `channelID`      | string | Yes      | The Slack channel ID to post to.                                               |
| `messageText`    | string | No       | Plain text message content. Required unless you set `blocks` or `attachments`. |
| `blocks`         | string | No       | A JSON string of Slack Block Kit blocks for rich formatting.                   |
| `threadTs`       | string | No       | The timestamp of a parent message to reply in a thread.                        |
| `replyBroadcast` | bool   | No       | Set to `true` to also post the reply to the channel. Requires `threadTs`.      |
| `attachments`    | string | No       | A JSON array string of Slack message attachments.                              |

**Outputs:**

Expand table

| Name        | Type   | Description            |
|-------------|--------|------------------------|
| `messageID` | string | The posted message ID. |

#### Archive Slack channel (`slack.channel.archive`)

Archive a Slack channel.

**Inputs:**

Expand table

| Name        | Type   | Required | Description                      |
|-------------|--------|----------|----------------------------------|
| `channelID` | string | Yes      | The Slack channel ID to archive. |

**Outputs:**

Expand table

| Name      | Type | Description                                      |
|-----------|------|--------------------------------------------------|
| `success` | bool | `true` if the channel was archived successfully. |

#### Add bookmark to Slack channel (`slack.channel.add-bookmark`)

Add a bookmark link to a Slack channel.

**Inputs:**

Expand table

| Name            | Type   | Required | Description                            |
|-----------------|--------|----------|----------------------------------------|
| `channelID`     | string | Yes      | The Slack channel ID.                  |
| `bookmarkTitle` | string | Yes      | The title for the bookmark.            |
| `bookmarkUrl`   | string | Yes      | The URL for the bookmark.              |
| `bookmarkEmoji` | string | No       | An emoji to display with the bookmark. |

**Outputs:**

Expand table

| Name         | Type   | Description                     |
|--------------|--------|---------------------------------|
| `bookmarkID` | string | The ID of the created bookmark. |

#### Add users to Slack channel (`slack.channel.add-users`)

Invite users to an existing Slack channel. You can identify each user by their Slack user ID or their email address, and you can mix both in one list. Workflows looks up each email address in Slack. If an email address doesn’t match a Slack user, the step fails and the error lists the unmatched addresses. If you set `users` with an expression and a step output already contains a list, wrap it in `json_stringify()`. For example, `${json_stringify(steps.oncall.outputs.users.map(u, u.email))}` uses the email addresses from a step with the ID `oncall`, such as **Get on-call users from a schedule**.

**Inputs:**

Expand table

| Name        | Type           | Required | Description                                            |
|-------------|----------------|----------|--------------------------------------------------------|
| `channelID` | string         | Yes      | The Slack channel ID.                                  |
| `users`     | array (string) | Yes      | A list of Slack user IDs or email addresses to invite. |

**Outputs:**

Expand table

| Name      | Type | Description                                  |
|-----------|------|----------------------------------------------|
| `success` | bool | `true` if the users were added successfully. |

#### Send Slack direct message (`slack.direct-message.send`)

Send a direct message to up to 8 Slack users. If you select more than one user, everyone receives the same message in a single group direct message. You can identify each user by their Slack user ID or their email address, and you can mix both in one list. Workflows looks up each email address in Slack. If an email address doesn’t match a Slack user, the step fails and the error lists the unmatched addresses. If you set `users` with an expression and a step output already contains a list, wrap it in `json_stringify()`. For example, `${json_stringify(steps.oncall.outputs.users.map(u, u.email))}` uses the email addresses from a step with the ID `oncall`, such as **Get on-call users from a schedule**.

**Inputs:**

Expand table

| Name      | Type           | Required | Description                                                     |
|-----------|----------------|----------|-----------------------------------------------------------------|
| `users`   | array (string) | Yes      | A list of up to 8 Slack user IDs or email addresses to message. |
| `message` | string         | Yes      | Plain text message content.                                     |

**Outputs:**

Expand table

| Name        | Type   | Description                                      |
|-------------|--------|--------------------------------------------------|
| `channelID` | string | The Slack conversation ID of the direct message. |
| `messageID` | string | The sent message ID.                             |

### IRM

IRM steps page users and teams, invite users to incidents and alert groups, and look up who’s on call.

#### Page user via IRM (`irm.page-user`)

Create an IRM escalation to page a user by email address.

**Inputs:**

Expand table

| Name        | Type   | Required | Description                                                             |
|-------------|--------|----------|-------------------------------------------------------------------------|
| `userID`    | string | Yes      | Email address of the user to page (for example, `alice@example.com`).   |
| `important` | bool   | No       | Set to `true` to mark the escalation as important. Defaults to `false`. |
| `message`   | string | No       | A message to attach to the escalation.                                  |

**Outputs:**

Expand table

| Name            | Type   | Description                                                               |
|-----------------|--------|---------------------------------------------------------------------------|
| `oncallUserID`  | string | The resolved IRM user ID used in the escalation.                          |
| `escalationID`  | string | The created escalation ID.                                                |
| `state`         | string | The escalation state returned by IRM, for example, `triggered`.           |
| `alertGroupURL` | string | A permalink to the related IRM alert group, if returned. Otherwise empty. |
| `error`         | string | An error message if the operation failed. Empty on success.               |

#### Page team via IRM (`irm.page-team`)

Create an IRM escalation to page an entire team. You can also associate the escalation with an incident, which invites the team to join it, and add a message.

**Inputs:**

Expand table

| Name             | Type   | Required | Description                                                                                |
|------------------|--------|----------|--------------------------------------------------------------------------------------------|
| `teamIdentifier` | string | Yes      | The team identifier value, interpreted according to `identifierType`.                      |
| `identifierType` | string | No       | Identifier mode: `id` (default, IRM team ID) or `name` (exact team name match).            |
| `incidentID`     | string | No       | An incident ID to associate with the escalation. The team is invited to join the incident. |
| `important`      | bool   | No       | Set to `true` to mark the escalation as important. Defaults to `false`.                    |
| `message`        | string | No       | A message to attach to the escalation.                                                     |

**Outputs:**

Expand table

| Name            | Type   | Description                                                               |
|-----------------|--------|---------------------------------------------------------------------------|
| `escalationID`  | string | The created escalation ID.                                                |
| `state`         | string | The escalation state returned by IRM, for example, `triggered`.           |
| `alertGroupURL` | string | A permalink to the related IRM alert group, if returned. Otherwise empty. |
| `error`         | string | An error message if the operation failed. Empty on success.               |

#### Invite user to incident (`irm.invite-user-to-incident`)

Create an IRM escalation to invite a user to an incident.

**Inputs:**

Expand table

| Name         | Type   | Required | Description                                                             |
|--------------|--------|----------|-------------------------------------------------------------------------|
| `userID`     | string | Yes      | Email address of the user to invite (for example, `alice@example.com`). |
| `incidentID` | string | Yes      | ID of the incident to associate with the escalation.                    |
| `important`  | bool   | No       | Set to `true` to mark the escalation as important. Defaults to `false`. |
| `message`    | string | No       | A message to attach to the escalation.                                  |

**Outputs:**

Expand table

| Name            | Type   | Description                                                           |
|-----------------|--------|-----------------------------------------------------------------------|
| `oncallUserID`  | string | The resolved IRM user ID used in the escalation.                      |
| `escalationID`  | string | The created escalation ID.                                            |
| `state`         | string | The escalation state returned by IRM, for example, `triggered`.       |
| `alertGroupURL` | string | A permalink to the related alert group, if returned. Otherwise empty. |
| `error`         | string | An error message if the operation failed. Empty on success.           |

#### Invite user to alert group (`irm.invite-user-to-alert-group`)

Create an IRM escalation to invite a user to an alert group.

**Inputs:**

Expand table

| Name           | Type   | Required | Description                                                             |
|----------------|--------|----------|-------------------------------------------------------------------------|
| `userID`       | string | Yes      | Email address of the user to invite (for example, `alice@example.com`). |
| `alertGroupID` | string | Yes      | ID of the alert group to associate with the escalation.                 |
| `important`    | bool   | No       | Set to `true` to mark the escalation as important. Defaults to `false`. |

**Outputs:**

Expand table

| Name            | Type   | Description                                                           |
|-----------------|--------|-----------------------------------------------------------------------|
| `oncallUserID`  | string | The resolved IRM user ID used in the escalation.                      |
| `escalationID`  | string | The created escalation ID.                                            |
| `state`         | string | The escalation state returned by IRM, for example, `triggered`.       |
| `alertGroupURL` | string | A permalink to the related alert group, if returned. Otherwise empty. |
| `error`         | string | An error message if the operation failed. Empty on success.           |

#### Get on-call users from a schedule (`irm.schedule-current-oncall`)

Look up the users currently on call for an IRM schedule. Use the result in later paging or notification steps.

**Inputs:**

Expand table

| Name                 | Type   | Required | Description                                                                    |
|----------------------|--------|----------|--------------------------------------------------------------------------------|
| `scheduleIdentifier` | string | Yes      | The schedule identifier value, interpreted according to `identifierType`.      |
| `identifierType`     | string | No       | Identifier mode: `id` (default, IRM schedule ID) or `name` (exact name match). |

**Outputs:**

Expand table

| Name         | Type   | Description                                                                               |
|--------------|--------|-------------------------------------------------------------------------------------------|
| `scheduleID` | string | The resolved IRM schedule ID used for the lookup.                                         |
| `count`      | int    | The number of on-call users returned. `0` when no one is on-call.                         |
| `users`      | array  | The list of on-call user objects, including `id`, `email`, `username`, `slack`, and more. |
| `shifts`     | array  | The list of current shift entries, including `shift_start`, `shift_end`, and `users`.     |
| `error`      | string | An error message if the operation failed. Empty on success.                               |

#### Get on-call users for a team (`irm.team-current-oncall`)

Look up the users currently on call for a team, using the team’s direct paging integration. Use this step when you want to notify whoever is on-call for a team without specifying a schedule.

**Inputs:**

Expand table

| Name             | Type   | Required | Description                                                                                           |
|------------------|--------|----------|-------------------------------------------------------------------------------------------------------|
| `teamIdentifier` | string | Yes      | The team identifier value, interpreted according to `identifierType`.                                 |
| `identifierType` | string | No       | Identifier mode: `id` (default, IRM team ID) or `name` (exact name match).                            |
| `route`          | string | No       | An optional route ID for the team’s direct paging integration. If omitted, the default route is used. |

**Outputs:**

Expand table

| Name     | Type   | Description                                                                               |
|----------|--------|-------------------------------------------------------------------------------------------|
| `teamID` | string | The resolved IRM team ID used for the lookup.                                             |
| `count`  | int    | The number of on-call users returned. `0` when no one is on-call.                         |
| `users`  | array  | The list of on-call user objects, including `id`, `email`, `username`, `slack`, and more. |
| `shifts` | array  | The list of current shift entries, including `shift_start`, `shift_end`, and `users`.     |
| `error`  | string | An error message if the operation failed. Empty on success.                               |

### Microsoft Teams

Steps that post messages to or create resources in Microsoft Teams.

**Prerequisites:**

Before using any Microsoft Teams step, install the Microsoft Teams app through Grafana IRM integrations and set up at least one hook for it. This connects your Grafana Cloud stack to your Microsoft Teams tenant. After the connection exists, you can turn off or remove the hook.

The `msteams.meeting.create` step needs the `Calendars.ReadWrite` application permission on the app registration used by your Microsoft Teams integration, with admin consent granted. New installs of the Microsoft Teams app request this permission and grant consent automatically, so you only need to add it if you installed the app before Workflows supported `msteams.meeting.create`. In that case, add the permission in the app registration’s API permissions page and grant admin consent for it. No additional Teams-specific admin setup is required.

#### Create Teams meeting (`msteams.meeting.create`)

Create a Microsoft Teams online meeting on behalf of an organizer. The meeting is added to the organizer’s Outlook calendar. Requires the `Calendars.ReadWrite` Microsoft Entra ID application permission. Refer to the Prerequisites above.

The calendar event shows the organizer as free, so it doesn’t block their time or conflict with other meetings.

To compute `startDateTime` or `endDateTime` relative to another timestamp, use the CEL `timestamp()` and `duration()` functions, for example, `${timestamp(inputs.time) + duration('1h')}`. Refer to [Use CEL expressions](/docs/grafana-cloud/platform/workflows/use-expressions/#use-built-in-functions).

**Inputs:**

Expand table

| Name             | Type   | Required | Description                                                                                                                                                                |
|------------------|--------|----------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `organizerEmail` | string | Yes      | Email address of the meeting organizer, matched against either their mail or userPrincipalName attribute in Microsoft Entra. The meeting is created on their behalf.       |
| `subject`        | string | Yes      | Meeting subject.                                                                                                                                                           |
| `startDateTime`  | string | Yes      | Meeting start time, RFC 3339 format, for example, `2026-06-11T10:00:00Z`. Any timezone offset is accepted and converted to UTC before the meeting is created.              |
| `endDateTime`    | string | No       | Meeting end time, RFC 3339 format, converted to UTC the same way as `startDateTime`. Must be after `startDateTime`. Defaults to one hour after `startDateTime` if not set. |

**Outputs:**

Expand table

| Name        | Type   | Description                                   |
|-------------|--------|-----------------------------------------------|
| `meetingID` | string | Calendar event ID for the created meeting.    |
| `joinURL`   | string | Join link to share with meeting participants. |

#### Post message to Teams channel (`msteams.message.post`)

Post a text message or Adaptive Card to a Microsoft Teams channel. At least one of `messageText` or `adaptiveCard` is required. To reply in a thread, pass `conversationID` and `threadActivityID` from a previous `msteams.message.post` step.

**Inputs:**

Expand table

| Name               | Type   | Required | Description                                                                                                                                              |
|--------------------|--------|----------|----------------------------------------------------------------------------------------------------------------------------------------------------------|
| `teamID`           | string | Yes      | Microsoft Teams team ID. Use the team picker or a CEL expression.                                                                                        |
| `channelID`        | string | Yes      | Microsoft Teams channel ID. The channel picker lists the channels in the selected team. Use the channel picker or a CEL expression.                      |
| `messageText`      | string | No       | Plain text message body. Required unless `adaptiveCard` is provided. When both are set, text is sent with the Adaptive Card.                             |
| `adaptiveCard`     | string | No       | JSON string of an Adaptive Card object (the card body). Required unless `messageText` is provided.                                                       |
| `conversationID`   | string | No       | Conversation ID from a prior `msteams.message.post` step, for example, `${steps['<STEP_ID>'].outputs.conversationID}`. Required with `threadActivityID`. |
| `threadActivityID` | string | No       | Activity ID of the parent message to reply to, for example, `${steps['<STEP_ID>'].outputs.activityID}`. Required with `conversationID`.                  |

**Outputs:**

Expand table

| Name             | Type   | Description                                                                                                                    |
|------------------|--------|--------------------------------------------------------------------------------------------------------------------------------|
| `conversationID` | string | Conversation ID for this message. Pass as `conversationID` on a later `msteams.message.post` step to reply in the same thread. |
| `activityID`     | string | Activity ID of the posted message. Pass as `threadActivityID` on a later `msteams.message.post` step to reply to this message. |

### Grafana Assistant

Steps that send a prompt to the Grafana Assistant and return structured output.

#### Ask Assistant (`assistant.prompt`)

Send a prompt to the assistant and receive structured JSON output conforming to a provided schema. If the assistant’s response doesn’t parse as JSON or doesn’t validate against `outputSchema`, the step sends a follow-up prompt asking the assistant to correct its response. If the chat request itself fails, the step sends the same prompt again. Both kinds of retry count toward `maxRetries`.

When the retries run out, the step fails and the run stops. The run error includes the last error and the beginning of the assistant’s last response.

**Prerequisites:**

Grafana Assistant must be in use on your stack. The person whose permissions the run uses needs the `grafana-assistant-app.chats:access` permission. For details, refer to [Manage access](/docs/grafana-cloud/platform/workflows/manage-access).

**Inputs:**

Expand table

| Name           | Type   | Required | Description                                                                                                   |
|----------------|--------|----------|---------------------------------------------------------------------------------------------------------------|
| `prompt`       | string | Yes      | The prompt to send to the assistant. Supports a CEL expression.                                               |
| `outputSchema` | string | No       | JSON Schema (draft-07 through 2020-12) describing the expected output structure. Defaults to any JSON object. |
| `maxRetries`   | int    | No       | Maximum number of retries. Defaults to 3, maximum 10.                                                         |

**Outputs:**

Expand table

| Name               | Type   | Description                                                 |
|--------------------|--------|-------------------------------------------------------------|
| `result`           | object | The validated JSON object from the assistant.               |
| `rawText`          | string | The raw text response before parsing, for debugging.        |
| `attempts`         | int    | Number of attempts taken. 1 means success on the first try. |
| `validationErrors` | string | Empty on success. Outputs aren’t saved for a failed step.   |

## Trigger types

Triggers define when a workflow starts. You configure triggers in the `startWhen` section of a workflow definition. For details on configuring triggers, refer to [Configure triggers](/docs/grafana-cloud/platform/workflows/configure-triggers).

### Event trigger

An event trigger starts a workflow when an incoming event matches a regular expression pattern. Events are named according to the pattern `{app}.{resource-type}.{event-type}`, for example, `grafana_irm_app.incident.updated`.

You define event triggers using matching rules. Each rule contains a regular expression that’s tested against the event name. If any rule matches, the workflow starts.

**Configuration:**

Expand table

| Field            | Type   | Required | Description                                                                                     |
|------------------|--------|----------|-------------------------------------------------------------------------------------------------|
| `eventNameRegex` | string | Yes      | A regular expression tested against the event name. The workflow starts if the pattern matches. |

**Example:**

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

```yaml
startWhen:
  matchingRules:
    - eventNameRegex: "grafana_irm_app\\.incident\\.updated"
    - eventNameRegex: "grafana_irm_app\\.alertgroup\\.updated"
```

#### Available event sources

The following table lists the event sources that can trigger workflows:

Expand table

| App               | Resource     | Event     | Example event name                   | Description                                          |
|-------------------|--------------|-----------|--------------------------------------|------------------------------------------------------|
| `grafana_irm_app` | `incident`   | `updated` | `grafana_irm_app.incident.updated`   | Fires any time an incident is created or updated.    |
| `grafana_irm_app` | `alertgroup` | `updated` | `grafana_irm_app.alertgroup.updated` | Fires any time an alert group is created or updated. |
| `grafana_irm_app` | `schedule`   | `updated` | `grafana_irm_app.schedule.updated`   | Fires when the current on-call users change.         |

### Schedule trigger

A schedule trigger starts a workflow at recurring times using a cron expression. Schedules use the standard five-field Unix cron format. They run in UTC unless the expression starts with `CRON_TZ=<ZONE>`. For details, refer to [Run a schedule in a specific timezone](/docs/grafana-cloud/platform/workflows/configure-triggers/#run-a-schedule-in-a-specific-timezone).

**Configuration:**

Expand table

| Field      | Type   | Required | Description                                                                                                       |
|------------|--------|----------|-------------------------------------------------------------------------------------------------------------------|
| (schedule) | string | Yes      | A five-field Unix cron expression, for example, `0 9 * * 1-5`. Runs in UTC unless prefixed with `CRON_TZ=<ZONE>`. |

**Example:**

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

```yaml
startWhen:
  schedules:
    - "0 9 * * 1-5"
    - "0 0 1 * *"
```

A workflow can have multiple schedules. Each schedule triggers the workflow independently.

**Cron field reference:**

Expand table

| Field        | Range             | Special characters |
|--------------|-------------------|--------------------|
| Minute       | 0-59              | `*`, `,`, `-`, `/` |
| Hour         | 0-23              | `*`, `,`, `-`, `/` |
| Day of month | 1-31              | `*`, `,`, `-`, `/` |
| Month        | 1-12              | `*`, `,`, `-`, `/` |
| Day of week  | 0-6 (0 is Sunday) | `*`, `,`, `-`, `/` |

### Manual trigger

A manual trigger starts a workflow from the editor on demand. You can trigger any workflow manually, whether or not it has event or schedule triggers.

To trigger a workflow manually in the editor, click **Run** and provide event data in CloudEvents format in the **Trigger Workflow** dialog box. For details, refer to [Configure triggers](/docs/grafana-cloud/platform/workflows/configure-triggers/#trigger-workflows-manually).

Manual-only workflows have an empty `startWhen` section:

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

```yaml
startWhen:
  matchingRules: []
  schedules: []
```

### Combine triggers

A workflow can have both event and schedule triggers. The workflow starts when any matching rule matches an incoming event or when any schedule fires.

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

```yaml
startWhen:
  matchingRules:
    - eventNameRegex: "grafana_irm_app\\.incident\\.updated"
  schedules:
    - "0 9 * * 1-5"
```
