Public preview Grafana Cloud
Last reviewed: October 13, 2026

Use the API

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.

Use the Grafana Workflows API to manage workflows from scripts, continuous integration (CI) jobs, and other systems. You can create and update workflows, run a workflow on demand, and read the result of every step.

The API has two parts:

  • The definitions endpoints create, read, update, and delete workflows.
  • The run endpoints run workflows and return their results.

For every parameter, field, and error code, refer to Workflows API reference.

Caution

The run endpoints might change during public preview. Build your scripts on the fields this page documents, and plan to update them before general availability.

Before you begin

Before you call the API, make sure you have:

  • A Grafana Cloud stack with the Workflows app installed.
  • A service account with the Editor basic role and edit access to the folder for your workflows. For details, refer to Manage access.
  • A token for that service account. In Grafana, navigate to Administration > Users and access > Service accounts to create one. For details, refer to Service accounts.
  • The UID of the folder for your workflows. Open the folder in Grafana and copy the UID from the URL, which has the form /dashboards/f/<FOLDER_UID>/.
  • A Bash-compatible shell, such as the terminal on macOS or Linux, or Windows Subsystem for Linux (WSL).
  • Optional: jq, a command-line JSON tool. Most examples use only curl. The examples that also use jq say so.

Set up your environment

Save your stack URL and token in environment variables. The examples on this page use them, and they last until you close the terminal.

Bash
export GRAFANA_URL="https://<STACK_SLUG>.grafana.net"
export GRAFANA_TOKEN="<SERVICE_ACCOUNT_TOKEN>"

Next, find your stack’s namespace, which has the form stacks-<STACK_ID>, for example, stacks-1234567. Your stack ID appears on your stack’s details page in the Grafana Cloud portal. If you have jq, you can also look up the namespace with the following command:

Bash
curl -s -H "Authorization: Bearer $GRAFANA_TOKEN" \
  "$GRAFANA_URL/api/frontend/settings" | jq -r .namespace

Save the namespace and the base URLs for the two parts of the API:

Bash
export NAMESPACE="stacks-<STACK_ID>"
export DEFS="$GRAFANA_URL/apis/workflows.ext.grafana.app/v1alpha1/namespaces/$NAMESPACE/definitions"
export RUNS_API="$GRAFANA_URL/api/plugins/grafana-workflows-app/resources/api/v1"

List workflows

To list every workflow the service account can access, send a GET request:

Bash
curl -s -H "Authorization: Bearer $GRAFANA_TOKEN" "$DEFS"

The response contains an items list with one definition for each workflow. The following example shows one workflow with no steps:

JSON
{
  "apiVersion": "workflows.ext.grafana.app/v1alpha1",
  "kind": "DefinitionList",
  "items": [
    {
      "apiVersion": "workflows.ext.grafana.app/v1alpha1",
      "kind": "Definition",
      "metadata": {
        "name": "incident-kickoff",
        "namespace": "stacks-1234567",
        "uid": "543a49fd-b2b8-4b91-8a68-ec2a2a5c5ea9",
        "resourceVersion": "2099486562503979008",
        "annotations": {
          "grafana.app/folder": "ffvibaiepdc74e"
        }
      },
      "spec": {
        "name": "Incident kickoff",
        "enabled": true,
        "startWhen": {
          "matchingRules": [
            {
              "eventNameRegex": "grafana_irm_app\\.incident\\.updated",
              "filter": "inputs.data.eventKind == 'incident_created'"
            }
          ],
          "schedules": []
        },
        "steps": []
      }
    }
  ]
}

Grafana adds metadata.uid and metadata.resourceVersion to every workflow. You need metadata.uid to read a workflow’s runs, and metadata.resourceVersion to replace a workflow.

To get one workflow, add its ID to the URL:

Bash
curl -s -H "Authorization: Bearer $GRAFANA_TOKEN" "$DEFS/incident-kickoff"

Create a workflow

To create a workflow, follow these steps:

  1. Save the following definition as hello-workflow.json, and replace FOLDER_UID with your folder UID.

    The workflow has no trigger, so it runs only when you start it. Its one step builds a greeting from the who input.

    JSON
    {
      "apiVersion": "workflows.ext.grafana.app/v1alpha1",
      "kind": "Definition",
      "metadata": {
        "name": "hello-workflow",
        "annotations": {
          "grafana.app/folder": "<FOLDER_UID>"
        }
      },
      "spec": {
        "name": "Hello workflow",
        "enabled": true,
        "startWhen": {
          "matchingRules": [],
          "schedules": []
        },
        "steps": [
          {
            "id": "greet",
            "name": "Build greeting",
            "type": "transform",
            "inputs": {
              "template": "Hello ${has(inputs.who) ? inputs.who : 'world'}"
            }
          }
        ]
      }
    }
  2. Send the file in a POST request:

    Bash
    curl -s -X POST \
      -H "Authorization: Bearer $GRAFANA_TOKEN" \
      -H "Content-Type: application/json" \
      --data @hello-workflow.json \
      "$DEFS"

    If the request succeeds, the response is the saved definition, including its metadata.uid. If the definition has a problem, the response is a 400 Bad Request error that names each problem:

    JSON
    {
      "kind": "Status",
      "status": "Failure",
      "message": "admission webhook \"validate-workflows.workflows.svc\" denied the request: step \"greet\" is missing required field \"template\" for type \"transform\"",
      "code": 400
    }
  3. Confirm the workflow exists. Open the Workflows app, go to Manage workflows, and find Hello workflow.

To add triggers, schedules, secrets, and branches to a definition, refer to Create workflows and Configure triggers.

Check a definition without saving it

To have Workflows check a definition without saving it, add ?dryRun=All to a create or update request. You can use this check in a CI job to catch mistakes before you merge a change.

Bash
curl -s -X POST \
  -H "Authorization: Bearer $GRAFANA_TOKEN" \
  -H "Content-Type: application/json" \
  --data @hello-workflow.json \
  "$DEFS?dryRun=All"

Update a workflow

You can update a workflow in two ways:

  • Replace the whole definition with a PUT request.
  • Patch only the fields you want to change with a PATCH request.

Replace a workflow

To replace a workflow, send the complete definition with the workflow’s current metadata.resourceVersion. Workflows uses the version to make sure you don’t overwrite a change that someone made after you read the workflow.

The following script uses jq. It reads the current resourceVersion, adds it to your file, and sends the result:

Bash
rv=$(curl -s -H "Authorization: Bearer $GRAFANA_TOKEN" "$DEFS/hello-workflow" \
  | jq -r .metadata.resourceVersion)

jq --arg rv "$rv" '.metadata.resourceVersion = $rv' hello-workflow.json \
  | curl -s -X PUT \
      -H "Authorization: Bearer $GRAFANA_TOKEN" \
      -H "Content-Type: application/json" \
      --data @- \
      "$DEFS/hello-workflow"

If someone changed the workflow after you read it, the API returns 409 Conflict. Get the workflow again, make your change again, and retry.

Turn a workflow on or off

To change a single field, send a merge patch, which contains only the fields you want to change. The following example turns off a workflow:

Bash
curl -s -X PATCH \
  -H "Authorization: Bearer $GRAFANA_TOKEN" \
  -H "Content-Type: application/merge-patch+json" \
  --data '{"spec": {"enabled": false}}' \
  "$DEFS/hello-workflow"

To turn the workflow back on, set enabled to true.

Note

A merge patch replaces a list as a whole. To change one step, send the complete steps list, or replace the workflow with a PUT request.

Delete a workflow

To delete a workflow, send a DELETE request to its URL:

Bash
curl -s -X DELETE \
  -H "Authorization: Bearer $GRAFANA_TOKEN" \
  "$DEFS/hello-workflow"

Run a workflow

To run a saved workflow, send a POST request to its run endpoint. Use the workflow’s ID in the URL. Turn on the workflow first.

Bash
curl -s -X POST \
  -H "Authorization: Bearer $GRAFANA_TOKEN" \
  -H "Content-Type: application/json" \
  --data '{"inputs": {"who": "the on-call team"}}' \
  "$RUNS_API/workflows/hello-workflow/run"

The request returns when the run starts, before it finishes. The response identifies the run:

JSON
{
  "definition_uid": "061a6f48-a841-4ab3-bfde-9c4ad2ca3a2d",
  "definition_name": "hello-workflow",
  "instance_id": "jc7565f6rw8r",
  "execution_id": "b9d8112d-ae3f-45ea-9bce-5f7cd8435d4e",
  "status": "active",
  "triggering_event": "api",
  "created_at": "2026-09-23T18:11:19Z",
  "steps": []
}

Save definition_uid, instance_id, and execution_id. You need all three to read the run’s result.

Pass inputs to a run

The inputs object in the request becomes the inputs object that steps read. In the preceding example, the step reads the value as inputs.who.

A workflow that normally starts from an event reads the event’s fields from inputs.data. To run one of these workflows with test data, put the fields in a data object:

JSON
{
  "inputs": {
    "data": {
      "incidentID": "123",
      "eventKind": "incident_created"
    }
  }
}

For the fields each event includes, refer to Sample event payloads.

Note

A run you start through the API isn’t a Grafana IRM event, so Workflows doesn’t load the incident or alert group for you. To use resources.incident.event in these runs, declare the incident in the definition:

YAML
spec:
  resources:
    incidents:
      event: "incident://id/${inputs.data.incidentID}"

When an IRM event starts the workflow, this declaration loads the same incident, so the workflow behaves the same way in both cases. For details, refer to Declare incident resources explicitly.

Test a draft definition

To run a definition without saving it first, send it to the trigger endpoint. The editor’s Run button uses this endpoint. The workflow doesn’t need to exist, and the draft runs even when it’s turned off. That makes the endpoint useful for testing changes before you save them.

Bash
curl -s -X POST \
  -H "Authorization: Bearer $GRAFANA_TOKEN" \
  -H "Content-Type: application/json" \
  --data @trigger-request.json \
  "$RUNS_API/workflows/hello-workflow/trigger"

The request contains the definition and an optional event. The following example leaves out the contents of spec. In your request, include the complete spec. Don’t send hello-workflow.json as this body. The trigger body holds the definition in a definition field.

JSON
{
  "definition": {
    "apiVersion": "workflows.ext.grafana.app/v1alpha1",
    "kind": "Definition",
    "metadata": {
      "name": "hello-workflow",
      "uid": "<DEFINITION_UID>"
    },
    "spec": {}
  },
  "event_data": {
    "id": "test-1",
    "type": "grafana-workflows.manual",
    "source": "my-script",
    "data": {
      "who": "the on-call team"
    }
  }
}

Steps read the fields under event_data.data as inputs.data.*. In this example, event_data.data.who is inputs.data.who.

If you’re testing changes to a saved workflow, include its metadata.uid. Without it, the run doesn’t appear in the workflow’s run history, and you can’t get its step results. You can still find the run in the list of all runs.

Get the result of a run

Runs finish in the background. To get a run’s result, request it with the three IDs from the run response:

Bash
curl -s -H "Authorization: Bearer $GRAFANA_TOKEN" \
  "$RUNS_API/runs/<DEFINITION_UID>/<INSTANCE_ID>/<EXECUTION_ID>"

The response contains the run’s status and one entry for each step that ran. The following example leaves out some fields and the contents of definition_spec, which holds the definition as it was when the run started:

JSON
{
  "definition_name": "hello-workflow",
  "status": "completed",
  "created_at": "2026-09-23T18:11:19Z",
  "completed_at": "2026-09-23T18:11:20Z",
  "steps": [
    {
      "name": "greet",
      "step_name": "Build greeting",
      "step_type": "transform",
      "status": "completed",
      "inputs": { "template": "Hello the on-call team" },
      "outputs": { "result": "Hello the on-call team" },
      "error": null
    }
  ],
  "definition_spec": {}
}

Each step’s inputs show the values after Workflows filled in expressions. Inputs that hold secrets show [REDACTED] instead of the value.

Wait for a run to finish

A run’s status is active while it runs, then completed or failed. The following script uses jq. It starts a run, checks its status every two seconds, and prints the result when the run finishes:

Bash
run=$(curl -s -X POST \
  -H "Authorization: Bearer $GRAFANA_TOKEN" \
  -H "Content-Type: application/json" \
  --data '{"inputs": {"who": "the on-call team"}}' \
  "$RUNS_API/workflows/hello-workflow/run")

run_path=$(echo "$run" | jq -r '"\(.definition_uid)/\(.instance_id)/\(.execution_id)"')

while true; do
  result=$(curl -s -H "Authorization: Bearer $GRAFANA_TOKEN" "$RUNS_API/runs/$run_path")
  run_status=$(echo "$result" | jq -r .status)
  if [ "$run_status" != "active" ]; then
    break
  fi
  sleep 2
done

echo "$result" | jq '{status, error_message, steps: [.steps[] | {name, status, outputs, error}]}'

When a run fails, the run’s error_message and each step’s error field explain why. For common causes, refer to Troubleshoot workflows.

List runs

To list recent runs across all workflows, send a GET request to the runs endpoint:

Bash
curl -s -H "Authorization: Bearer $GRAFANA_TOKEN" "$RUNS_API/runs?count=10"

To list the runs of one workflow, add its metadata.uid to the path. The following example lists only failed runs:

Bash
curl -s -H "Authorization: Bearer $GRAFANA_TOKEN" \
  "$RUNS_API/runs/<DEFINITION_UID>?status=failed"

The response lists the newest runs first:

JSON
{
  "runs": [
    {
      "definition_uid": "061a6f48-a841-4ab3-bfde-9c4ad2ca3a2d",
      "definition_name": "hello-workflow",
      "definition_display_name": "Hello workflow",
      "instance_id": "jc7565f6rw8r",
      "execution_id": "b9d8112d-ae3f-45ea-9bce-5f7cd8435d4e",
      "status": "completed",
      "triggering_event": "api",
      "created_at": "2026-09-23T18:11:19Z",
      "completed_at": "2026-09-23T18:11:20Z"
    }
  ],
  "next_cursor": "jc7565f6rw8r:b9d8112d-ae3f-45ea-9bce-5f7cd8435d4e"
}

When more runs exist, the response includes next_cursor. To get the next page, pass that value in the after parameter. The list doesn’t include step results. To get them, request each run as described in Get the result of a run.

For all the filters you can use, refer to List runs parameters.

Next steps