id: jira name: Jira version: 1.4.0 description: "Atlassian Jira (REST API, Cloud and On-Prem/Data Center) — issue lifecycle (create/read/edit/delete, transitions, assign), comments, web + issue links, attachments (upload/download/delete), field and user lookups, agile boards/sprints/epics, plus issue ingestion (get_incidents), a change watch over a set of tickets (watch_issues) with an OCSF mapper. Basic (email + API token) or Personal Access Token authentication. Stdlib-only, no extra Python dependencies." changelog: "1.4.0 — New command watch_issues: given up to 50 issue keys and an instant, it returns the ones changed since, each with its current status, the comments added since (author id and name, plain-text body) and the status transitions from its changelog, plus the identity of the account the instance runs as, so a caller can tell its own comments from the customer's. It is what lets Riposte follow the ticket a case opened. JQL reads a date in the account's time zone and to the minute: the instant is converted to that zone and moved a minute back, and the exact cut is made on the timestamps Jira returns. Keys go into the JQL only when they look like keys (PROJECT-123); anything else is reported, not queried. A key that no longer exists fails a JQL query outright, so the keys are then read one by one and the dead ones reported under missing. get_comments takes order (oldest, the default and the previous behaviour, or newest) and start_at, returns total and next_start_at to page, and adds author_id (accountId on Cloud, key or name On-Prem). It read only the first page, oldest first, so past 50 comments the newest were never returned. Its body text keeps line breaks and decodes HTML entities: an & came out as &. | 1.3.0 — create_issue and edit_issue offer the Jira site's own values: the project, the issue type of that project and the priority are picked from a list the playbook editor loads from the instance, as is link_issues' link type. Three read commands feed them — list_projects, list_issue_types and list_priorities — and each field keeps accepting a typed value or an expression. 1.2.0 — Declare the risk tier on the 11 commands that carried none: 8 safe_write, 3 destructive. An unset risk is read as destructive by the agent approval gate, so the 8 that only annotate sat behind the same prompt as the ones that change the estate. | 1.1.0 — New command list_filters: the saved filters a Jira instance already holds, with their id, JQL and owner, plus the exact clause to paste into a fetch. A saved filter could always be ingested — filter = 12345 is ordinary JQL — but nothing told you the id, so the workflow was invisible. get_incidents and query_issues now mention it in their help. 1.0.2 — Command field help rewritten: every input now says what to type, in what format, and which command finds the value, instead of restating the field name. get_incidents explains that the fetch adds its own created >= watermark clause and ORDER BY, that the watermark is filled by the alert rule, and that a run does not page. Assignee, board, sprint, comment and attachment fields name the command that returns their id. 1.0.1 — A personal access token silently outranked Basic credentials, so an unusable value left in pat turned every call into an opaque 401. Basic authentication now wins whenever username and api_key are both set, pat serves the rest, and a 401/403 names the method it used and whether the other was configured and ignored. Three configurations that cannot work are refused with the reason instead of being sent to Jira: no usable credential, the Cloud gateway URL without a cloud_id, and a cloud_id set against any other host. HTML error pages are condensed to their message rather than dumped whole, and a non-JSON body reports what answered and where. 1.0.0 — Initial release: JQL ingestion + query, full issue CRUD and transitions, comments, links, attachments, fields/users, and agile board/sprint/epic listing. Cloud (API v3, ADF bodies) and On-Prem (API v2) supported." category: ticketing # Per-instance configuration. For Jira Cloud set cloud_id (found at # admin.atlassian.com/s//users) and keep the default server_url; for # On-Prem/Data Center leave cloud_id empty and set server_url to your Jira URL. # Authentication: Basic (username = account email + api_key = API token) OR a # Personal Access Token (pat). Provide only one method. config_schema: properties: server_url: type: string description: "Base URL. Jira Cloud: keep the default and set cloud_id. On-Prem/Data Center: your own Jira URL, e.g. https://jira.example.com, with cloud_id left empty." default: https://api.atlassian.com/ex/jira cloud_id: type: string description: "Jira Cloud ID, found at admin.atlassian.com/s//users. Required alongside the Cloud gateway URL; leave empty for On-Prem/Data Center." username: type: string description: "Account email (Basic auth, Jira Cloud). Pairs with api_key: both must be set for Basic authentication to be used." api_key: type: string description: "API token (Basic auth, Jira Cloud). Created at id.atlassian.com/manage-profile/security/api-tokens. Pairs with username." x-soar-sensitive: true pat: type: string description: "Personal Access Token (On-Prem/Data Center only). Used when username and api_key are not both set. Leave empty on Cloud." x-soar-sensitive: true required: - server_url commands: # ── Ingestion / search ──────────────────────────────────────────────────── - id: get_incidents name: jira-get-incidents description: "Fetch Jira issues matching a JQL query for ingestion. Returns {result:[...flattened issues...]}; use result as the alert rule results path." risk: read inputs_schema: properties: query: { type: string, description: "JQL selecting the issues to ingest, e.g. project = SOC AND status != Done — or filter = 12345 to reuse a saved Jira filter, whose id list_filters gives you. Empty means every issue the account can read. The fetch appends its own created >= watermark clause and ORDER BY created ASC — do not write either yourself." } after: { type: string, description: "Only issues created at or after this instant: ISO8601 like 2026-09-01T14:00:00Z, or an epoch in seconds or milliseconds. Leave empty — the alert rule fills it with the previous run's watermark." } max: { type: number, description: "Issues per run, default 50. The fetch makes one call and does not page, so a run that would exceed this silently drops the rest: raise it, or shorten the rule's interval." } required: [] outputs_schema: { properties: {} } ingest: results_path: result dedup_key: id incremental_field: after - id: query_issues name: jira-query-issues description: "Run a JQL query and return flattened issues." risk: read inputs_schema: properties: query: { type: string, description: "JQL, e.g. project = SOC AND created >= -7d ORDER BY created DESC, or filter = 12345 to run a saved filter (list_filters gives the id). Unlike the ingestion fetch, the ordering here is yours to set." } max_results: { type: number, description: "Issues per page, default 50." } next_page_token: { type: string, description: "Cloud only. The nextPageToken from the previous response, to get the page after it. Empty for the first page." } start_at: { type: number, description: "On-Prem only. Index of the first issue: 0, then 50, then 100… Cloud pages with next_page_token instead." } required: [query] outputs_schema: { properties: {} } - id: list_filters name: jira-list-filters description: "List the saved Jira filters that can be referenced from a JQL query. Returns each filter's id, name, JQL and owner, plus jql_reference — the clause to paste straight into a fetch or a query." risk: read inputs_schema: properties: name: { type: string, description: "Keep only filters whose name contains this text, e.g. SOC. Empty lists them all." } limit: { type: number, description: "Filters per page, default 50." } offset: { type: number, description: "Index of the first row, for paging: 0, then 50, then 100…" } required: [] outputs_schema: { properties: {} } - id: watch_issues name: jira-watch-issues description: "Report what changed on a set of issues since an instant: for each issue updated since, its status, the comments added since and its status transitions. Also returns self, the account the instance runs as, and flags its own comments. Built for Riposte's ITSM watch, which follows the ticket recorded on a case." risk: read inputs_schema: properties: keys: { type: array, description: "Issue keys to watch, at most 50, e.g. [SOC-1423, SOC-1430]. A comma-separated string is read too. A value that is not a key (PROJECT-123) is not queried and comes back under invalid_keys." } since: { type: string, description: "Only changes at or after this instant: ISO8601 like 2026-10-13T12:30:00Z, or an epoch in seconds or milliseconds. Empty means a first look: every issue, with its latest page of comments, to record as already seen." } required: [keys] outputs_schema: { properties: {} } - id: get_issue name: jira-get-issue description: "Retrieve a single issue by ID or key (flattened)." risk: read inputs_schema: properties: issue_id: { type: string, description: "Numeric issue ID, e.g. 10042 — the id field the fetch returns. Give this or issue_key." } issue_key: { type: string, description: "Issue key as shown in Jira, e.g. SOC-1423. Give this or issue_id." } required: [] outputs_schema: { properties: {} } # ── Issue lifecycle ─────────────────────────────────────────────────────── - id: create_issue name: jira-create-issue description: "Create a new issue. Provide project_key (or project_name), issue_type_name (or issue_type_id) and a summary." risk: safe_write inputs_schema: properties: project_key: type: string description: "Project key — the prefix of its issue keys, so SOC for SOC-1423. Give this or project_name." options_from: { command: list_projects, path: projects, value: key, label: name } project_name: { type: string, description: "Project display name, e.g. Security Operations. It must match exactly one project. Give this or project_key, which is the faster path." } issue_type_name: type: string description: "Issue type as the target project names it, e.g. Task, Bug, Incident. Types are per-project. Give this or issue_type_id." options_from: { command: list_issue_types, path: issue_types, value: name, label: name, depends_on: [project_key] } issue_type_id: { type: string, description: "Numeric issue type ID, e.g. 10004. Give this or issue_type_name, which is usually easier to read." } summary: { type: string, description: "The issue title, one line. This is what shows in every Jira list." } description: { type: string, description: "Body text. Write plain text: it is converted to Atlassian Document Format on Cloud." } priority: type: string description: "Priority name exactly as the project defines it, e.g. Highest, High, Medium. An unknown name is rejected." options_from: { command: list_priorities, path: priorities, value: name, label: name } labels: { type: string, description: "Comma-separated, no space inside a label, e.g. soc,phishing. Unknown labels are created." } components: { type: string, description: "Comma-separated, e.g. Network,Endpoint. Components must already exist in the project — Jira will not create them." } assignee_id: { type: string, description: "Cloud: the account ID, e.g. 5b10a2844c20165700ede21g. get_id_by_attribute turns a name or email into one." } assignee: { type: string, description: "On-Prem: the username, e.g. jdupont. get_id_by_attribute turns a name or email into one." } due_date: { type: string, description: "Date only, yyyy-mm-dd, e.g. 2026-09-30." } fields_json: { type: string, description: "Advanced. A JSON object merged into Jira's fields payload, for anything the boxes above do not cover, e.g. {\"customfield_10010\": \"SOC-2\"}. Its keys win over those boxes." } required: [summary] outputs_schema: { properties: {} } - id: edit_issue name: jira-edit-issue description: "Edit an issue's fields, and/or move it with status/transition (see jira-list-transitions)." risk: safe_write inputs_schema: properties: issue_id: { type: string, description: "Numeric issue ID, e.g. 10042 — the id field the fetch returns. Give this or issue_key." } issue_key: { type: string, description: "Issue key as shown in Jira, e.g. SOC-1423. Give this or issue_id." } summary: { type: string, description: "Replacement title. Leave empty to keep the current one." } description: { type: string, description: "Replacement body, plain text. Replaces the whole description; it does not append." } priority: type: string description: "Priority name exactly as the project defines it, e.g. Highest, High, Medium. An unknown name is rejected." options_from: { command: list_priorities, path: priorities, value: name, label: name } labels: { type: string, description: "Comma-separated, e.g. soc,phishing. Replaces the whole list: a label left out is removed." } components: { type: string, description: "Comma-separated, e.g. Network,Endpoint. Replaces the whole list, and each must already exist in the project." } assignee_id: { type: string, description: "Cloud: the account ID, e.g. 5b10a2844c20165700ede21g. get_id_by_attribute turns a name or email into one." } assignee: { type: string, description: "On-Prem: the username, e.g. jdupont. get_id_by_attribute turns a name or email into one." } due_date: { type: string, description: "Date only, yyyy-mm-dd, e.g. 2026-09-30." } status: { type: string, description: "Status you want the issue to end in, e.g. Done. The command finds a transition that leads there. Run list_transitions on the issue to see what it allows right now. Use this or transition, not both." } transition: { type: string, description: "Transition to apply by name, e.g. Start Progress — one of those list_transitions returns for this issue. Use this or status, not both." } fields_json: { type: string, description: "Advanced. A JSON object merged into Jira's fields payload, for anything the boxes above do not cover, e.g. {\"customfield_10010\": \"SOC-2\"}. Its keys win over those boxes." } required: [] outputs_schema: { properties: {} } - id: delete_issue name: jira-delete-issue description: "Delete an issue (its sub-tasks are deleted too)." risk: destructive inputs_schema: properties: issue_id: { type: string, description: "Numeric issue ID, e.g. 10042 — the id field the fetch returns. Give this or issue_key." } issue_key: { type: string, description: "Issue key as shown in Jira, e.g. SOC-1423. Give this or issue_id." } required: [] outputs_schema: { properties: {} } - id: assign_issue name: jira-assign-issue description: "Assign an issue to a user (assignee_id for Cloud, assignee for On-Prem)." risk: safe_write inputs_schema: properties: issue_id: { type: string, description: "Numeric issue ID, e.g. 10042 — the id field the fetch returns. Give this or issue_key." } issue_key: { type: string, description: "Issue key as shown in Jira, e.g. SOC-1423. Give this or issue_id." } assignee_id: { type: string, description: "Cloud: the account ID, e.g. 5b10a2844c20165700ede21g. get_id_by_attribute turns a name or email into one." } assignee: { type: string, description: "On-Prem: the username, e.g. jdupont. get_id_by_attribute turns a name or email into one." } required: [] outputs_schema: { properties: {} } - id: list_transitions name: jira-list-transitions description: "List the transitions available for an issue in its current status." risk: read inputs_schema: properties: issue_id: { type: string, description: "Numeric issue ID, e.g. 10042 — the id field the fetch returns. Give this or issue_key." } issue_key: { type: string, description: "Issue key as shown in Jira, e.g. SOC-1423. Give this or issue_id." } required: [] outputs_schema: { properties: {} } # ── Comments ────────────────────────────────────────────────────────────── - id: add_comment name: jira-add-comment description: "Add a comment to an issue." risk: safe_write inputs_schema: properties: issue_id: { type: string, description: "Numeric issue ID, e.g. 10042 — the id field the fetch returns. Give this or issue_key." } issue_key: { type: string, description: "Issue key as shown in Jira, e.g. SOC-1423. Give this or issue_id." } comment: { type: string, description: "Comment text, plain — it is converted to Atlassian Document Format on Cloud." } visibility: { type: string, description: "Project role the comment is limited to, by name, e.g. Administrators. Empty means anyone who can see the issue can read it." } required: [comment] outputs_schema: { properties: {} } - id: get_comments name: jira-get-comments description: "Return the comments of an issue." risk: read inputs_schema: properties: issue_id: { type: string, description: "Numeric issue ID, e.g. 10042 — the id field the fetch returns. Give this or issue_key." } issue_key: { type: string, description: "Issue key as shown in Jira, e.g. SOC-1423. Give this or issue_id." } limit: { type: number, description: "Comments per page, default 50." } order: { type: string, enum: [oldest, newest], description: "oldest (default) returns the first comments first; newest returns the latest first, which is what you want on a long thread." } start_at: { type: number, description: "Index of the first comment, for paging: 0, then the next_start_at the previous call returned. Empty for the first page." } required: [] outputs_schema: { properties: {} } - id: edit_comment name: jira-edit-comment description: "Edit an existing comment on an issue." risk: safe_write inputs_schema: properties: issue_id: { type: string, description: "Numeric issue ID, e.g. 10042 — the id field the fetch returns. Give this or issue_key." } issue_key: { type: string, description: "Issue key as shown in Jira, e.g. SOC-1423. Give this or issue_id." } comment_id: { type: string, description: "Numeric comment ID, e.g. 10231 — returned by get_comments." } comment: { type: string, description: "Replacement text, plain. Replaces the comment entirely." } visibility: { type: string, description: "Project role the comment is limited to, by name, e.g. Administrators. Empty means anyone who can see the issue can read it." } required: [comment_id, comment] outputs_schema: { properties: {} } - id: delete_comment name: jira-delete-comment description: "Delete a comment from an issue." risk: destructive inputs_schema: properties: issue_id: { type: string, description: "Numeric issue ID, e.g. 10042 — the id field the fetch returns. Give this or issue_key." } issue_key: { type: string, description: "Issue key as shown in Jira, e.g. SOC-1423. Give this or issue_id." } comment_id: { type: string, description: "Numeric comment ID, e.g. 10231 — returned by get_comments." } required: [comment_id] outputs_schema: { properties: {} } # ── Links ───────────────────────────────────────────────────────────────── - id: add_link name: jira-add-link description: "Add a remote web link (URL) to an issue." risk: safe_write inputs_schema: properties: issue_id: { type: string, description: "Numeric issue ID, e.g. 10042 — the id field the fetch returns. Give this or issue_key." } issue_key: { type: string, description: "Issue key as shown in Jira, e.g. SOC-1423. Give this or issue_id." } url: { type: string, description: "Full URL, scheme included, e.g. https://riposte.example.com/incidents/482." } title: { type: string, description: "The clickable text on the issue, e.g. Riposte incident 482." } summary: { type: string, description: "One line shown under the title. Optional." } relationship: { type: string, description: "How the link reads on the issue, e.g. causes, relates to, documents. Free text; empty leaves Jira's default wording." } required: [url, title] outputs_schema: { properties: {} } - id: link_issues name: jira-link-issues description: "Create a link between two issues (see jira-get-link-types for link_type names)." risk: safe_write inputs_schema: properties: inward_issue: { type: string, description: "One end of the link, key or numeric ID, e.g. SOC-1423. get_link_types gives each type's inward and outward wording, which tells you which issue goes on which side." } outward_issue: { type: string, description: "The other end, key or numeric ID, e.g. SOC-1500. See get_link_types for which side is which." } link_type: type: string description: "Link type by name, e.g. Blocks, Relates, Duplicate — exactly as get_link_types returns it." options_from: { command: get_link_types, path: link_types, value: name, label: name } comment: { type: string, description: "Comment recorded alongside the link. Optional." } required: [inward_issue, outward_issue, link_type] outputs_schema: { properties: {} } - id: get_link_types name: jira-get-link-types description: "List all issue link types." risk: read inputs_schema: properties: {} required: [] outputs_schema: { properties: {} } # ── Attachments ─────────────────────────────────────────────────────────── - id: upload_file name: jira-upload-file description: "Attach a file to an issue (content provided as base64)." risk: safe_write inputs_schema: properties: issue_id: { type: string, description: "Numeric issue ID, e.g. 10042 — the id field the fetch returns. Give this or issue_key." } issue_key: { type: string, description: "Issue key as shown in Jira, e.g. SOC-1423. Give this or issue_id." } file_name: { type: string, description: "Name the file gets in Jira, e.g. report.pdf. Its extension decides the content type sent to Jira." } content_base64: { type: string, description: "The file's bytes, base64-encoded. In a playbook this comes from an earlier block's file output rather than being typed." } required: [file_name, content_base64] outputs_schema: { properties: {} } - id: get_attachment name: jira-get-attachment description: "Download an attachment's content by attachment ID (returned as base64)." risk: read inputs_schema: properties: attachment_id: { type: string, description: "Numeric attachment ID, e.g. 10520 — get_issue lists them under attachments." } required: [attachment_id] outputs_schema: { properties: {} } - id: delete_attachment name: jira-delete-attachment description: "Delete an attachment by attachment ID." risk: destructive inputs_schema: properties: attachment_id: { type: string, description: "Numeric attachment ID, e.g. 10520 — get_issue lists them under attachments." } required: [attachment_id] outputs_schema: { properties: {} } # ── Fields / users ──────────────────────────────────────────────────────── - id: list_projects name: jira-list-projects description: "List the projects the account can see, with their key and name. Feeds the project picker of create_issue." risk: read inputs_schema: properties: {} required: [] outputs_schema: { properties: {} } - id: list_issue_types name: jira-list-issue-types description: "List the issue types of one project — the ones create_issue accepts there — or every type of the site when no project is given." risk: read inputs_schema: properties: project_key: { type: string, description: "Project key, e.g. SOC. Empty lists the site's types, some of which a given project may refuse." } required: [] outputs_schema: { properties: {} } - id: list_priorities name: jira-list-priorities description: "List the priorities defined on the site, by name." risk: read inputs_schema: properties: {} required: [] outputs_schema: { properties: {} } - id: list_fields name: jira-list-fields description: "List the system and custom issue fields." risk: read inputs_schema: properties: limit: { type: number, description: "Fields per page, default 50." } offset: { type: number, description: "Index of the first row, for paging: 0, then 50, then 100…" } required: [] outputs_schema: { properties: {} } - id: get_id_by_attribute name: jira-get-id-by-attribute description: "Resolve a user's account ID (Cloud) or username (On-Prem) from a display name or email." risk: read inputs_schema: properties: attribute: { type: string, description: "A display name or an email, e.g. Jean Dupont or jean.dupont@example.com. Returns the account ID (Cloud) or username (On-Prem) that the assignee boxes expect." } max_results: { type: number, description: "How many matches to weigh before giving up on an exact one, default 50." } required: [attribute] outputs_schema: { properties: {} } - id: get_user_info name: jira-get-user-info description: "Get a user's details (account_id for Cloud; key or username for On-Prem). With no identifier, returns the caller." risk: read inputs_schema: properties: account_id: { type: string, description: "Cloud: the account ID, e.g. 5b10a2844c20165700ede21g. One of the three is enough." } key: { type: string, description: "On-Prem: the user key, e.g. JIRAUSER10100. One of the three is enough." } username: { type: string, description: "On-Prem: the username, e.g. jdupont. One of the three is enough." } required: [] outputs_schema: { properties: {} } # ── Agile: boards / sprints / epics ─────────────────────────────────────── - id: board_list name: jira-board-list description: "List agile boards (optionally filter by name, project or type)." risk: read inputs_schema: properties: board_id: { type: string, description: "Numeric board ID to return on its own, e.g. 42. Empty lists them all." } board_name: { type: string, description: "Keep only boards whose name contains this text, e.g. SOC." } project_key_id: { type: string, description: "Keep only boards of one project, by key or numeric ID, e.g. SOC." } type: { type: string, description: "One of scrum, kanban or simple. Empty returns every type." } limit: { type: number, description: "Boards per page, default 50." } offset: { type: number, description: "Index of the first row, for paging: 0, then 50, then 100…" } required: [] outputs_schema: { properties: {} } - id: board_issue_list name: jira-board-issue-list description: "List the issues of a board (flattened)." risk: read inputs_schema: properties: board_id: { type: string, description: "Numeric board ID, e.g. 42 — returned by board_list." } jql_query: { type: string, description: "Extra JQL narrowing the result, e.g. status = \"In Progress\" AND assignee IS EMPTY. Applied on top of the filter this list already has." } limit: { type: number, description: "Issues per page, default 50." } offset: { type: number, description: "Index of the first row, for paging: 0, then 50, then 100…" } required: [board_id] outputs_schema: { properties: {} } - id: board_sprint_list name: jira-board-sprint-list description: "List the sprints of a board." risk: read inputs_schema: properties: board_id: { type: string, description: "Numeric board ID, e.g. 42 — returned by board_list." } limit: { type: number, description: "Sprints per page, default 50." } offset: { type: number, description: "Index of the first row, for paging: 0, then 50, then 100…" } required: [board_id] outputs_schema: { properties: {} } - id: sprint_issue_list name: jira-sprint-issue-list description: "List the issues of a sprint (flattened)." risk: read inputs_schema: properties: sprint_id: { type: string, description: "Numeric sprint ID, e.g. 137 — returned by board_sprint_list." } jql_query: { type: string, description: "Extra JQL narrowing the result, e.g. status = \"In Progress\" AND assignee IS EMPTY. Applied on top of the filter this list already has." } limit: { type: number, description: "Issues per page, default 50." } offset: { type: number, description: "Index of the first row, for paging: 0, then 50, then 100…" } required: [sprint_id] outputs_schema: { properties: {} } - id: epic_issue_list name: jira-epic-issue-list description: "List the issues that belong to an epic (flattened)." risk: read inputs_schema: properties: epic_id: { type: string, description: "Numeric epic ID, e.g. 10310. Give this or epic_key." } epic_key: { type: string, description: "Epic key, e.g. SOC-900. Give this or epic_id." } jql_query: { type: string, description: "Extra JQL narrowing the result, e.g. status = \"In Progress\" AND assignee IS EMPTY. Applied on top of the filter this list already has." } limit: { type: number, description: "Issues per page, default 50." } offset: { type: number, description: "Index of the first row, for paging: 0, then 50, then 100…" } required: [] outputs_schema: { properties: {} } - id: test_connection name: jira-test-connection description: "Verify connectivity and credentials by reading the current user (used by the Test button)." risk: read inputs_schema: properties: {} required: [] outputs_schema: { properties: {} } ingestion: command: get_incidents mapper: get_incidents default_incident_type: "Jira Issue"