Jira SLA Report in Google Sheets: Met, Breached, At Risk
To build a Jira SLA report in Google Sheets, import one JQL query per SLA outcome (met, breached, at risk) onto separate tabs, count each tab on a summary tab, and calculate the met rate as met divided by met plus breached. Jira’s SLA functions, such as everBreached(), completed() and remaining(), do the classification inside Jira. That matters because Atlassian’s free Sheets add-on shows each SLA as one remaining-time number with no status.
Below are the queries, the formulas, two ways to add goal and elapsed time per request, and the reasons a sheet’s numbers can disagree with Jira’s own reports. Facts are as of October 2026.
Check Jira’s built-in reports first
Every Jira Service Management service space comes with custom reports, according to Atlassian’s documentation, including SLA met vs breached, SLA success rate, Time to resolution and Created vs resolved. Open Reports in your service space. If one of those charts answers the question, use it; it reads the same SLA engine your agents see.
A sheet earns its place when you need to:
- combine SLA results with data from elsewhere, such as staffing or survey scores;
- report several service spaces in one table;
- keep a week-by-week history in a format you control;
- share results with people who don’t have Jira access.
Why a plain import isn’t enough
Atlassian’s tracker entry API-387, open since March 2021 and still “Gathering Interest”, describes the problem: in Jira Cloud for Sheets, an SLA column only shows a time, positive while time remains and negative once breached. Nothing says whether the SLA is active, paused, met or breached, or what its goal was.
So let JQL decide which requests met or breached, and give each outcome its own tab.
Tab layout
| Tab | Contents |
|---|---|
| Summary | Met rate, counts by priority and request type, notes |
| Met | Import: resolved last week, SLA met |
| Breached | Import: resolved last week, SLA breached |
| At risk | Import: open, breaching within two hours |
| Open breached | Import: open and already past the goal |
| History | One row of numbers per week |
Use the same columns on every import tab, in the same order: Key, Summary, Priority, Request Type, Assignee, Created, Updated, Resolved, and the SLA field.
The JQL for each tab
Replace HELP with your project key and use your own SLA names in double quotes; they’re set per space. Test each query in Filters > Search work items first.
Met last week:
project = HELP AND "Time to resolution" = completed() AND "Time to resolution" != everBreached() AND resolved >= startOfWeek("-1") AND resolved < startOfWeek()
Breached last week:
project = HELP AND "Time to resolution" = everBreached() AND resolved >= startOfWeek("-1") AND resolved < startOfWeek()
Using everBreached() in both queries keeps them complementary: a request that missed its goal in any cycle counts as breached once. If you want only the final cycle to count, use breached() in both. Pick one rule and write it on the summary tab.
At risk now:
project = HELP AND "Time to resolution" = running() AND "Time to resolution" < remaining("2h") AND "Time to resolution" != breached()
Open and already breached:
project = HELP AND statusCategory != Done AND "Time to resolution" = breached()
Waiting on a first response for over four hours (Atlassian’s own example uses one hour):
project = HELP AND "Time to first response" > elapsed("4h")
Paused clocks, often requests waiting on the customer:
project = HELP AND "Time to resolution" = paused()
Two adjustments you may need:
- Week start.
startOfWeek()begins on Sunday unless your site uses the ISO 8601 date picker setting. Atlassian documentsstartOfWeek("+1d")for Monday. - Team-managed service spaces. They have no resolution field, so
resolvedwon’t help. UsestatusCategory = DonewithstatusCategoryChangedDate >= -7dfor the date range.
More queries for other report types are in our JQL examples for reports.
Import the tabs
With Atlassian’s free Jira Cloud for Sheets add-on:
- Create the tabs from the layout above.
- On each import tab, open Extensions > Jira Cloud for Sheets > Open > Get data from Jira, paste the tab’s query, choose the columns, and select Get data.
- Set each import to refresh daily from the sidebar’s schedule tab, early enough to finish before anyone reads the report.
Build the summary
Counts and the met rate, with the formulas in column B:
B2 Met: =COUNTA(Met!A2:A)
B3 Breached: =COUNTA(Breached!A2:A)
B4 Met rate: =IFERROR(B2/(B2+B3), "")
B5 At risk: =COUNTA('At risk'!A2:A)
B6 Open past goal: =COUNTA('Open breached'!A2:A)
Met rate by priority, with priority names in A10:A14 and Priority in column C of both tabs:
B10: =IFERROR(COUNTIF(Met!C:C, A10) / (COUNTIF(Met!C:C, A10) + COUNTIF(Breached!C:C, A10)), "")
Fill it down. Point the same formula at the Request Type column to see whether misses cluster in particular request types.
Add goal and elapsed time per request
The tabs tell you which requests met or missed. Two methods add how long each one actually took.
Option A: copy elapsed time into a field, no code
An Atlassian knowledge base article for Jira Cloud (updated July 9, 2026) describes a scheduled automation rule that copies each SLA’s elapsed time into a single-line text field, using the smart value {{issue.Time to first response.completedCycles.elapsedTime.friendly}}. You create one text field per SLA; the field then appears in CSV exports, and you can add it to an import like any other field. Creating custom fields usually needs a Jira admin, and the value arrives as readable text such as a duration, so convert it before doing math.
Option B: read every SLA cycle with Apps Script
Jira Service Management’s REST API returns each SLA on a request with its completed cycles and its ongoing cycle: whether it breached, the goal, elapsed and remaining time in milliseconds, and whether it’s paused. The account you use must be an agent on the service space.
- Create an API token at id.atlassian.com/manage-profile/security/api-tokens with Create API token (not the scoped option, so the script can call your site URL). New tokens expire after at most a year.
- In the sheet, open Extensions > Apps Script. Under Project Settings, add script properties
JIRA_SITE(for exampleyour-site.atlassian.net),JIRA_EMAILandJIRA_TOKEN. Anyone who can edit the sheet can read them, so keep the editor list short. - Paste the script, change the JQL, and run
importSlaCyclesonce to authorize it. - Under Triggers, add a weekly time-driven trigger for Monday morning.
function importSlaCycles() {
const props = PropertiesService.getScriptProperties();
const site = props.getProperty('JIRA_SITE');
const auth = 'Basic ' + Utilities.base64Encode(
props.getProperty('JIRA_EMAIL') + ':' + props.getProperty('JIRA_TOKEN'));
const jql = 'project = HELP AND resolved >= startOfWeek("-1") AND resolved < startOfWeek()';
const hours = function (d) { return d ? d.millis / 36e5 : ''; };
const rows = [['Key', 'Summary', 'Priority', 'SLA', 'Cycle', 'Breached',
'Goal (h)', 'Elapsed (h)', 'Remaining (h)', 'Paused']];
searchIssues(site, auth, jql, ['summary', 'priority']).forEach(function (issue) {
const f = issue.fields;
const base = [issue.key, f.summary, f.priority ? f.priority.name : ''];
const slas = getJson(site, auth, '/rest/servicedeskapi/request/' + issue.key + '/sla?limit=50');
slas.values.forEach(function (sla) {
(sla.completedCycles || []).forEach(function (c) {
rows.push(base.concat([sla.name, 'Completed', c.breached, hours(c.goalDuration),
hours(c.elapsedTime), hours(c.remainingTime), false]));
});
const o = sla.ongoingCycle;
if (o) {
rows.push(base.concat([sla.name, 'Ongoing', o.breached, hours(o.goalDuration),
hours(o.elapsedTime), hours(o.remainingTime), o.paused]));
}
});
});
const ss = SpreadsheetApp.getActive();
const sheet = ss.getSheetByName('SLA cycles') || ss.insertSheet('SLA cycles');
sheet.clearContents();
sheet.getRange(1, 1, rows.length, rows[0].length).setValues(rows);
}
function searchIssues(site, auth, jql, fields) {
const issues = [];
let token = null;
do {
let path = '/rest/api/3/search/jql?maxResults=100&fields=' + fields.join(',') +
'&jql=' + encodeURIComponent(jql);
if (token) path += '&nextPageToken=' + encodeURIComponent(token);
const data = getJson(site, auth, path);
Array.prototype.push.apply(issues, data.issues);
token = data.nextPageToken;
} while (token);
return issues;
}
function getJson(site, auth, path) {
const res = UrlFetchApp.fetch('https://' + site + path, {
headers: { Authorization: auth, Accept: 'application/json' },
muteHttpExceptions: true,
});
if (res.getResponseCode() !== 200) {
throw new Error('Jira returned ' + res.getResponseCode() + ': ' +
res.getContentText().slice(0, 300));
}
return JSON.parse(res.getContentText());
}
The script makes one request per service request, so a few hundred requests per run is comfortable. Google limits each run to 6 minutes and URL fetches to 20,000 a day on personal accounts and 100,000 on Google Workspace accounts. If Jira refuses a call, the script stops with the error, and Apps Script’s trigger failure emails tell you.
Met rate and average hours from the SLA cycles tab:
=COUNTIFS('SLA cycles'!D:D, "Time to resolution", 'SLA cycles'!E:E, "Completed", 'SLA cycles'!F:F, FALSE) / COUNTIFS('SLA cycles'!D:D, "Time to resolution", 'SLA cycles'!E:E, "Completed")
=AVERAGEIFS('SLA cycles'!H:H, 'SLA cycles'!D:D, "Time to resolution", 'SLA cycles'!E:E, "Completed")
This counts SLA cycles, not requests, so a request with two completed cycles counts twice and the result won’t match the JQL tabs exactly. Decide which one you report, and label it.
Why your numbers may not match Jira’s
- Calendars and time zones. Atlassian says SLA calculations follow the time zone of the SLA calendar, while JQL dates follow the searcher’s profile. Requests resolved near midnight on the week boundary can land in different weeks.
- Last cycle or any cycle.
breached()andeverBreached()give different answers for reopened requests. - Different periods. Jira’s reports and your sheet may group by different dates. Define the period by resolution date and check one week by hand.
- Several SLAs. Report Time to first response and Time to resolution separately. Mixing them into one rate hides which one is failing.
- Permissions. Results reflect the account that runs the import.
Keep a weekly history and check freshness
Imports overwrite themselves, so save the summary numbers each week. The short History snapshot script in our weekly status report guide works here too; change its range to your summary cells, such as Summary!B2:B6.
An SLA report from yesterday’s data is misleading in a morning stand-up. Add a “newest change in this data” cell with MAX over the Updated column, and if a scheduled refresh didn’t run, see our Jira Cloud for Sheets troubleshooting guide.
Where Rowduet fits
Rowduet (in development) is a Google Sheets add-on being built for recurring reports like this one: JQL imports on an hourly or daily schedule, and an email when a refresh fails, so a stale SLA number doesn’t reach the meeting unnoticed. It isn’t available yet. If you run service desk reports from Sheets, you can join early access.
Frequently asked questions
Can I export Jira Service Management SLAs to Google Sheets?
Yes. Atlassian's free Jira Cloud for Sheets add-on imports SLA fields, but shows each one as a single remaining-time number. To report met and breached requests, import separate JQL queries that use breached(), everBreached() and completed(), or use a script that reads Jira Service Management's SLA REST endpoint.
What is the difference between breached() and everBreached() in JQL?
breached() returns requests whose most recent SLA cycle missed its goal. everBreached() returns requests that missed the goal in any cycle, which matters when a reopened request restarts the SLA clock.
How do I calculate an SLA met percentage in Google Sheets?
Divide met requests by met plus breached requests for the same period, for example =IFERROR(B2/(B2+B3), "") where B2 and B3 count the Met and Breached tabs. Leave requests whose SLA is still running out of the calculation.
Does Jira Service Management have a built-in SLA report?
Yes. Service spaces include custom reports such as SLA met vs breached, SLA success rate and Time to resolution. Use them for charts inside Jira, and use Sheets when you need to combine SLA data with other sources or share it with people who don't use Jira.