Sync Jira issue with incidents in Grafana IRM
This guide explains how to set up three outgoing webhooks in Grafana IRM to automatically create, update, and close Jira issues during an incident’s lifecycle.
Using IRM’s webhook and templating system to call Jira’s REST API, you can configure the following webhooks to keep Jira issues in sync with corresponding incidents in Grafana IRM:
- Create a Jira issue when an incident is declared
- Update the issue as the incident changes
- Close the issue when the incident is resolved
Workflow overview
The following sequence diagram shows how IRM and Jira exchange data during an incident:
sequenceDiagram
participant User as On-call responder
participant IRM as Grafana IRM
participant Jira as Jira REST API
User->>IRM: Declare incident
IRM->>Jira: POST /rest/api/2/issue<br/>[Creation webhook]
Jira-->>IRM: 201 Created<br/>{ "key": "SUP-17", ... }
Note right of IRM: Store issue key under<br/>responses["<CREATION_WEBHOOK_UUID>"].key
User->>IRM: Update incident (severity, title, labels, etc.)
IRM->>Jira: PUT /rest/api/2/issue/{{ responses["<CREATION_WEBHOOK_UUID>"].key }}<br/>[Update webhook]
Jira-->>IRM: 204 No Content
User->>IRM: Resolve incident
IRM->>Jira: POST /rest/api/2/issue/{{ responses["<CREATION_WEBHOOK_UUID>"].key }}/transitions<br/>[Closure webhook]
Jira-->>IRM: 204 No Content<br/>(Issue transitioned to a final state)Before you begin
Before configuring the webhooks, ensure you have the following:
- Grafana IRM with permission to configure Outgoing webhooks.
- A Jira instance with:
- A project where incident issues will be created (for example
SUP). - A suitable issue type (for example
TaskorIncident) that exists in the project.
- A project where incident issues will be created (for example
- A Jira account with permission to:
- Create issues in the target project.
- Edit issues in the target project.
- Transition issues in the target workflow.
Jira authentication
All webhooks use Basic authentication with a Jira username and API token.
To create a Jira API token:
- Go to https://id.atlassian.com/manage-profile/security/api-tokens.
- Select Create API token.
- Copy the token and store it securely.
- Use your Jira email and API token in each webhook’s authentication settings.
Jira instance information
You need the following information from your Jira instance:
Base URL, including protocol, for example:
<JIRA_BASE_URL> = https://your-domain.atlassian.netProject key, for example:
<JIRA_PROJECT_KEY> = SUPIssue type, for example:
<JIRA_ISSUE_TYPE> = TaskorIncidentVerify that this issue type exists in your Jira project before configuring the webhook.
All URLs in this guide use <JIRA_BASE_URL> directly, for example:
<JIRA_BASE_URL>/rest/api/2/issuePlaceholder reference
This guide uses the following placeholders:
Dynamic Jira issue key retrieval
To update or close a Jira issue, you need the issue key (for example SUP-17) from the creation webhook’s response.
The creation webhook returns the JSON response from a newly created issue. IRM stores this response under a unique webhook ID.
IRM exposes these responses in templates through a responses object:
responsesis a map of webhook UUID → last JSON response.<CREATION_WEBHOOK_UUID>is the UUID of the creation webhook.- Jira’s create-issue API returns the issue key as the
keyfield in the response.
{{ responses["<CREATION_WEBHOOK_UUID>"].key }}Use this expression in the URL of the Update and Close webhooks to target the same Jira issue created by the creation webhook.
For example:
<JIRA_BASE_URL>/rest/api/2/issue/{{ responses["webhook-out-6400aeb030670e95"].key }}To find the webhook UUID:
- Save the creation webhook.
- Open the creation webhook’s dropdown menu (three dots).
- Copy the Webhook UUID value.
You can confirm the stored response by going to Alerts & IRM → IRM → Integrations → Outgoing webhooks and inspecting a successful execution of the creation webhook.
Transition ID for closing issues
To configure the closure webhook, you must determine the project’s transition ID and a valid resolution name.
Note
Transition IDs are specific to the issue type and workflow. Use an issue of the same type you plan to create in your webhook configuration.
Identify any issue in your project with the same issue type you’ll use in the webhook, for example
SUP-17.Call Jira’s transitions API with your Jira email and API token:
curl -u "<JIRA_EMAIL>:<JIRA_API_TOKEN>" \ -H "Accept: application/json" \ "<JIRA_BASE_URL>/rest/api/2/issue/SUP-17/transitions"Locate the transition that matches your expected final state.
Note its
idvalue. Use this ID as<TRANSITION_ID>in the closure webhook.Query available resolutions for your project:
curl -u "<JIRA_EMAIL>:<JIRA_API_TOKEN>" \ -H "Accept: application/json" \ "<JIRA_BASE_URL>/rest/api/2/resolution"Choose a valid resolution name from the response (for example
Done,Fixed, orClosed). Use this as<JIRA_RESOLUTION_NAME>in the closure payloads.
Configure the creation webhook
Creates a new Jira issue when an incident is declared in IRM.
Webhook summary:
To create the webhook in IRM, follow these steps:
In Grafana, go to Alerts & IRM → IRM → Integrations → Outgoing webhooks.
Select Create an Outgoing Webhook and choose Advanced webhook for incidents.
Set the following fields:
- Enabled: On
- Trigger type: Incident declared
- HTTP method: POST
- Webhook URL:
<JIRA_BASE_URL>/rest/api/2/issue - Forward whole payload data: Off
- Customize forwarded data: On
- Authentication: Basic
- Username: Jira email
- Password: Jira API token
In the Data textbox, paste the following JSON request body:
{
"fields": {
"project": { "key": "<JIRA_PROJECT_KEY>" },
"issuetype": { "name": "<JIRA_ISSUE_TYPE>" },
"summary": "{{ incident.severity | upper }}: {{ incident.title }}",
"description": "*Incident ID:* {{ incident.incidentID }}\\n"
"*Severity:* {{ incident.severity }}\\n"
"*Status:* {{ incident.status }}\\n"
"*Started:* {{ incident.incidentStart }}\\n\\n"
"----\\n"
"*Roles*\\n"
"{% if incident.incidentMembership.assignments and incident.incidentMembership.assignments|length > 0 %}"
"{% for a in incident.incidentMembership.assignments %}"
"- {{ a.role.name }}: {{ a.user.name }}\\n"
"{% endfor %}"
"{% else %}_No roles recorded._{% endif %}\\n\\n"
"*Tasks*\\n"
"{% if incident.taskList.tasks and incident.taskList.tasks|length > 0 %}"
"{% for t in incident.taskList.tasks %}- {{ t.text }}\\n{% endfor %}"
"{% else %}_No tasks recorded._{% endif %}\\n\\n"
"*Timeline*\\n"
"Created: {{ incident.createdTime }}\\n\\n"
"[View in Grafana IRM]({{ incident.overviewURL }})",
"labels": [
{%- for label_item in incident.labels -%}
"{{ label_item.label }}"{%- if not loop.last -%},{% endif -%}
{%- endfor -%}
]
}
}This payload includes mandatory fields (project, issuetype) and uses precise Jinja templating to extract simple string labels from IRM’s label object structure.
Note
The template engine renders this body before Jira receives it. After rendering, the result must be valid JSON.
If the incident has no roles, tasks, or labels, the template renders empty sections or empty arrays, which is valid JSON.
After saving the webhook, open the menu (three dots) and copy its Webhook UUID for use in the next webhook URLs as <CREATION_WEBHOOK_UUID>.
For more details, refer to the Dynamic Jira issue key retrieval section earlier in this document.
Configure the update webhook
Synchronizes the Jira issue with ongoing changes to the incident in IRM.
The webhook runs when an incident is updated. Supported update events include:
- Title
- Severity
- Status
- Roles
- Labels
For more information, refer to the Outgoing webhook documentation.
Webhook summary:
To create the webhook in IRM, complete the following steps:
In Grafana, go to Alerts & IRM → IRM → Integrations → Outgoing webhooks.
Select Create an Outgoing Webhook and choose Advanced webhook for incidents.
Set the following fields:
- Enabled: On
- Trigger type: Incident changed
- HTTP method: PUT
- Webhook URL:
<JIRA_BASE_URL>/rest/api/2/issue/{{ responses["<CREATION_WEBHOOK_UUID>"].key }} - Forward whole payload data: Off
- Customize forwarded data: On
- Authentication: Basic (same Jira email and API token as the creation webhook)
In the Data textbox, paste the following JSON request body:
{
"fields": {
"summary": "{{ incident.severity | upper }}: {{ incident.title }}",
"description": "*Incident ID:* {{ incident.incidentID }}\\n"
"*Severity:* {{ incident.severity }}\\n"
"*Status:* {{ incident.status }}\\n"
"*Started:* {{ incident.incidentStart }}\\n\\n"
"----\\n"
"*Roles*\\n"
"{% if incident.incidentMembership.assignments and incident.incidentMembership.assignments|length > 0 %}"
"{% for a in incident.incidentMembership.assignments %}"
"- {{ a.role.name }}: {{ a.user.name }}\\n"
"{% endfor %}"
"{% else %}_No roles recorded._{% endif %}\\n\\n"
"*Tasks*\\n"
"{% if incident.taskList.tasks and incident.taskList.tasks|length > 0 %}"
"{% for t in incident.taskList.tasks %}- {{ t.text }}\\n{% endfor %}"
"{% else %}_No tasks recorded._{% endif %}\\n\\n"
"*Timeline*\\n"
"Updated: {{ incident.modifiedTime }}\\n\\n"
"[View in Grafana IRM]({{ incident.overviewURL }})",
"labels": [
{%- for label_item in incident.labels -%}
"{{ label_item.label }}"{%- if not loop.last -%},{% endif -%}
{%- endfor -%}
]
}
}This payload focuses only on fields intended for update. The PUT method will overwrite these fields in Jira. Adapt the template as needed based on your Jira workflow.
Configure the closure webhook
Transitions the Jira issue to a final workflow state when an incident is resolved.
Webhook summary:
To create the webhook in IRM, complete the following steps:
In Grafana, go to Alerts & IRM → IRM → Integrations → Outgoing webhooks.
Select Create an Outgoing Webhook and choose Advanced webhook for incidents.
Set the following fields:
- Enabled: On
- Trigger type: Incident resolved
- HTTP method: POST
- Webhook URL:
<JIRA_BASE_URL>/rest/api/2/issue/{{ responses["<CREATION_WEBHOOK_UUID>"].key }}/transitions - Forward whole payload data: Off
- Customize forwarded data: On
- Authentication: Basic (same Jira email and API token as the other webhooks)
In the Data textbox, paste one of the following JSON request bodies:
Minimal closure payload:
{
"transition": {
"id": "<TRANSITION_ID>"
},
"fields": {
"resolution": {
"name": "<JIRA_RESOLUTION_NAME>"
}
}
}Optional payload with a comment:
If your team requires an automatic closing comment or summary, use the update object. The comment field is not permitted directly in the fields block during a transition.
{
"transition": {
"id": "<TRANSITION_ID>"
},
"update": {
"comment": [
{
"add": {
"body": "Incident closed automatically from Grafana IRM.\\n\\nSummary at closure: {{ incident.summary }}"
}
}
]
},
"fields": {
"resolution": {
"name": "<JIRA_RESOLUTION_NAME>"
}
}
}Note
- Replace
<TRANSITION_ID>with the value obtained from the/transitionsAPI.- Replace
<JIRA_RESOLUTION_NAME>with a valid resolution name for your Jira project (for exampleDone,Fixed, or a project-specific resolution). If the resolution name does not exist, Jira returns an error.For more information, refer to the Transition ID retrieval section earlier in this document.
Test the configuration
After configuring all three webhooks, test each one to verify they work as expected.
Test incident creation:
- Declare a new incident in IRM.
- Verify that a Jira issue appears in your target project.
- Confirm the summary, description, and labels match the incident.
Test incident update:
- Modify the incident (for example, change severity or add labels).
- Confirm the linked Jira issue reflects the changes.
Test incident closure:
- Resolve the incident in IRM.
- Confirm the Jira issue transitions to the expected final state and resolution.
You can inspect request and response details in Alerts & IRM → IRM → Integrations → Outgoing webhooks → Event details.
By configuring these three webhooks, Grafana IRM keeps Jira issues in sync throughout the lifecycle of the incident. This maintains shared context across responders, automates handoff to ticketing workflows, and reduces manual updates.
Troubleshooting
If you encounter issues when configuring or using the Jira webhooks, refer to the following guidance to resolve common problems.
Jira issue key not found
If update or closure webhooks fail with a 404 or invalid issue key error, verify the following:
Ensure the creation webhook succeeded and returned a valid response.
Confirm the
<CREATION_WEBHOOK_UUID>in your templates matches the actual webhook UUID.In Event details, open a successful execution of the creation webhook and verify that the response JSON contains a top-level
keyfield.Confirm the update and closure URLs use the same UUID:
<JIRA_BASE_URL>/rest/api/2/issue/{{ responses["<CREATION_WEBHOOK_UUID>"].key }}
Authentication errors (401/403)
If webhooks fail with authentication errors, check the following:
- Verify the Jira email and API token in the webhook authentication settings.
- Confirm the Jira user has permission to:
- Create issues in the target project.
- Edit issues in the target project.
- Transition issues using the specified
<TRANSITION_ID>.
- If your organization uses SSO, ensure you are using an API token, not an SSO password.
Transition ID or resolution errors
If the closure webhook returns 400 errors related to transitions or resolution, verify the following:
- Confirm that
<TRANSITION_ID>is valid for the issue’s current status. An issue may not be able to transition directly from its current status to the final state you selected. - Verify that
<JIRA_RESOLUTION_NAME>matches an existing resolution name in your Jira project. - Use the transitions API again for the specific issue to confirm valid transitions at that point in the workflow.
- Review the Jira API error message in Execution history for details.
Template rendering errors
If webhook execution fails before sending a request, or if Execution history shows template evaluation errors (for example, “Object of type Undefined is not JSON serializable”), check the following:
- Ensure the request body renders to valid JSON. Common issues include:
- Adjacent string literals without commas.
- Missing or extra commas after Jinja control blocks.
- Verify Jinja template syntax:
- Ensure there is a matching
{% endfor %}for each{% for %}and{% endif %}for each{% if %}.
- Ensure there is a matching
- Confirm fields like
incident.incidentMembership.assignments,incident.taskList.tasks, andincident.labelsexist for the current incident:- These fields may be null or empty for new incidents. The template handles empty states, but verify the field structure matches your incident data.
- Use Execution history for the creation webhook to inspect the full incident payload.
- If your payload uses different field names, update the template accordingly.
- If needed, simplify the template temporarily (for example, remove the roles/tasks sections) to verify that the basic JSON structure is valid, then add complexity back step by step.
For additional information, refer to Outgoing webhooks in the Grafana Cloud documentation.



