# Jira issue status changed notifications on your phone > Get a push notification when a Jira issue moves to another status, or only when it's done. - Company: Jira (https://www.justpush.io/recipes/jira) - Event: `jira:issue_updated` (Issue: updated) - Tags: Project management, Issues & tickets - Install: https://studio.justpush.io/recipes/jira/issue-status - Web page: https://www.justpush.io/recipes/jira/issue-status ## Setup 1. Click Install in Studio and sign in. The recipe is added to your account and you get a personal webhook URL. 2. In Jira, go to Settings > System > WebHooks and click Create a WebHook (Jira admins only). 3. Paste your webhook URL (shown in Studio after install) as the URL. Optionally limit it with a JQL filter such as project = ABC. 4. Tick Issue: updated and save. 5. Create an admin webhook under **Settings → System → WebHooks** and tick **Issue: updated**. Every status change pushes; to only hear about finished work (statuses in the **Done** category), use: 6. ``` 7. your webhook URL (shown in Studio after install)?done=1 8. ``` 9. You can also narrow it with the webhook's JQL box, for example `project = SHOP AND type = Bug`. 10. Admin webhooks can have a **Secret**, which Jira uses to sign each delivery (`X-Hub-Signature: sha256=…`), but JustPush doesn't check that signature yet, so keep your endpoint URL private. ## Code Studio calls `handleRequest(request)` with the incoming webhook (`{ method, headers, body, query, raw }`) and sends the message object it returns. Returning `null` sends nothing. ```js // Jira → jira:issue_updated (issue status changed) // Fires when an issue moves to another status (To Do → In Progress → Done …). // Add ?done=1 to the URL to only hear about issues moving into a Done-category status. // Docs: https://developer.atlassian.com/cloud/jira/platform/webhooks/ // Status categories (new, indeterminate, done) → emoji. const CATEGORIES = { new: "📋", indeterminate: "🚧", done: "✅" } // Look a key up in a table without tripping over inherited names like "toString". function pick(table, key, fallback) { return Object.prototype.hasOwnProperty.call(table, key) ? table[key] : fallback } // Only link to real http(s) URLs. function link(url, cta) { return typeof url === "string" && /^https?:\/\//.test(url) ? [{ cta, url }] : [] } // Query flags such as ?done=1. function flag(request, name) { return ["1", "true", "yes"].includes(String(request.query?.[name] ?? "").toLowerCase()) } // issue.self is the REST URL (https://acme.atlassian.net/rest/api/2/issue/10001); the browse // link lives on the same site: https://acme.atlassian.net/browse/ABC-123. function browse(issue) { const match = typeof issue?.self === "string" ? issue.self.match(/^(https?:\/\/.+?)\/rest\//) : null return match && typeof issue.key === "string" ? `${match[1]}/browse/${issue.key}` : null } function handleRequest(request) { const body = request.body && typeof request.body === "object" ? request.body : {} const issue = body.issue && typeof body.issue === "object" ? body.issue : null const items = Array.isArray(body.changelog?.items) ? body.changelog.items : [] const change = items.find((item) => item && (item.fieldId === "status" || item.field === "status")) const fields = issue?.fields ?? {} // Read the changelog's own "toString", never Object.prototype.toString. const changed = change ? pick(change, "toString", null) : null const status = typeof changed === "string" && changed ? changed : fields.status?.name // Guard: only issue updates where the status changed. if (body.webhookEvent !== "jira:issue_updated" || !issue || !change || typeof status !== "string" || !status) { console.log("Not a Jira status change — skipping") return null } const category = fields.status?.statusCategory?.key // ?done=1 limits pushes to issues landing in a Done-category status. if (flag(request, "done") && category !== "done") { console.log(`Skipping move to "${status}"`) return null } const details = [ fields.summary, fields.assignee?.displayName, body.user?.displayName ? `by ${body.user.displayName}` : null, ].filter((value) => typeof value === "string" && value) return { title: `${pick(CATEGORIES, category, "🔁")} ${issue.key ? `${issue.key} ` : ""}→ ${status}`, message: details.join(" · ") || "Status changed", topic: "Jira", priority: category === "done" ? 0 : -1, // Done is worth a look; other moves are quiet buttons: link(browse(issue), "Open issue"), } } ``` ## Jira jira:issue_updated webhook payload (sample) ```json { "user": { "self": "https://acme.atlassian.net/rest/api/2/user?accountId=5b10ac8d82e05b22cc7d4ef7", "active": true, "accountId": "5b10ac8d82e05b22cc7d4ef7", "accountType": "atlassian", "displayName": "Jane Doe" }, "issue": { "id": "10231", "key": "SHOP-88", "self": "https://acme.atlassian.net/rest/api/2/issue/10231", "fields": { "status": { "id": "10002", "name": "Done", "statusCategory": { "id": 3, "key": "done", "name": "Done" } }, "project": { "id": "10000", "key": "SHOP", "name": "Webshop" }, "summary": "Checkout total is off by one cent", "assignee": { "active": true, "accountId": "5b10a2844c20165700ede21g", "displayName": "Sam Lee" }, "priority": { "id": "2", "name": "High" }, "issuetype": { "id": "10004", "name": "Bug" } } }, "changelog": { "id": "10187", "items": [ { "to": "10002", "from": "3", "field": "status", "fieldId": "status", "toString": "Done", "fieldtype": "jira", "fromString": "In Progress" }, { "to": "10000", "from": null, "field": "resolution", "fieldId": "resolution", "toString": "Done", "fieldtype": "jira", "fromString": null } ] }, "timestamp": 1759058400000, "webhookEvent": "jira:issue_updated", "issue_event_type_name": "issue_generic" } ``` ## FAQ ### Does this work on iPhone and Android? Yes. Install the JustPush app from the App Store or Google Play and sign in. Every phone signed in to your account gets the notification. ### Do I need to write code? No. Install the recipe in Studio, paste your webhook URL into Jira and you are done. The code is there if you want to change the text, the sound or the buttons. ### Can I change what the notification says? Yes. After install the recipe's code is yours. Edit it in Studio and test it against the sample payload before you save. ### What does it cost? JustPush is free for 30 days. After that it's $19.99 a year, or $39.99 once. There is no extra charge for recipes.