id: jira
name: Jira
version: 1.0.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) with an OCSF mapper. Basic (email + API token) or Personal Access Token authentication. Stdlib-only, no extra Python dependencies."
changelog: "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/<cloud_id>/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: https://api.atlassian.com/ex/jira (default). On-Prem: your Jira server URL."
      default: https://api.atlassian.com/ex/jira
    cloud_id:
      type: string
      description: "Jira Cloud ID (leave empty for On-Prem/Data Center)"
    username:
      type: string
      description: "Account email (Basic auth)"
    api_key:
      type: string
      description: "API token (Basic auth)"
      x-soar-sensitive: true
    pat:
      type: string
      description: "Personal Access Token (use instead of username/api_key)"
      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 query (e.g. status != Done). Do not add an ORDER BY — the fetch adds one." }
        after: { type: string, description: "Lower bound on created time, ISO8601 or epoch (incremental fetch watermark, applied as created >= ...)" }
        max: { type: number, description: "Maximum issues to fetch (default 50)" }
      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 query string" }
        max_results: { type: number, description: "Maximum issues (default 50)" }
        next_page_token: { type: string, description: "Token for the next page (Jira Cloud only)" }
        start_at: { type: number, description: "Index of the first issue (On-Prem; Cloud uses next_page_token)" }
      required: [query]
    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: "Issue ID (issue_id or issue_key required)" }
        issue_key: { type: string, description: "Issue key, e.g. PROJ-123 (issue_id or issue_key required)" }
      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."
    inputs_schema:
      properties:
        project_key: { type: string, description: "Project key (project_key or project_name required)" }
        project_name: { type: string, description: "Project name (resolved to a key; project_key or project_name required)" }
        issue_type_name: { type: string, description: "Issue type name, e.g. Task, Bug (issue_type_name or issue_type_id required)" }
        issue_type_id: { type: string, description: "Issue type ID (issue_type_name or issue_type_id required)" }
        summary: { type: string, description: "Issue summary (required)" }
        description: { type: string, description: "Issue description" }
        priority: { type: string, description: "Priority name, e.g. High, Medium" }
        labels: { type: string, description: "Comma-separated labels" }
        components: { type: string, description: "Comma-separated component names (must already exist)" }
        assignee_id: { type: string, description: "Assignee account ID (Cloud)" }
        assignee: { type: string, description: "Assignee username (On-Prem)" }
        due_date: { type: string, description: "Due date (yyyy-mm-dd)" }
        fields_json: { type: string, description: "Raw fields JSON merged into the request (advanced; wins on key conflicts)" }
      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)."
    inputs_schema:
      properties:
        issue_id: { type: string, description: "Issue ID (issue_id or issue_key required)" }
        issue_key: { type: string, description: "Issue key (issue_id or issue_key required)" }
        summary: { type: string, description: "New summary" }
        description: { type: string, description: "New description" }
        priority: { type: string, description: "Priority name" }
        labels: { type: string, description: "Comma-separated labels (replaces existing)" }
        components: { type: string, description: "Comma-separated component names (replaces existing)" }
        assignee_id: { type: string, description: "Assignee account ID (Cloud)" }
        assignee: { type: string, description: "Assignee username (On-Prem)" }
        due_date: { type: string, description: "Due date (yyyy-mm-dd)" }
        status: { type: string, description: "Target status name — transitions the issue (mutually exclusive with transition)" }
        transition: { type: string, description: "Transition name to apply (mutually exclusive with status)" }
        fields_json: { type: string, description: "Raw fields JSON merged into the request (advanced)" }
      required: []
    outputs_schema: { properties: {} }
  - id: delete_issue
    name: jira-delete-issue
    description: "Delete an issue (its sub-tasks are deleted too)."
    inputs_schema:
      properties:
        issue_id: { type: string, description: "Issue ID (issue_id or issue_key required)" }
        issue_key: { type: string, description: "Issue key (issue_id or issue_key required)" }
      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)."
    inputs_schema:
      properties:
        issue_id: { type: string, description: "Issue ID (issue_id or issue_key required)" }
        issue_key: { type: string, description: "Issue key (issue_id or issue_key required)" }
        assignee_id: { type: string, description: "Assignee account ID (Cloud)" }
        assignee: { type: string, description: "Assignee username (On-Prem)" }
      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: "Issue ID (issue_id or issue_key required)" }
        issue_key: { type: string, description: "Issue key (issue_id or issue_key required)" }
      required: []
    outputs_schema: { properties: {} }

  # ── Comments ──────────────────────────────────────────────────────────────
  - id: add_comment
    name: jira-add-comment
    description: "Add a comment to an issue."
    inputs_schema:
      properties:
        issue_id: { type: string, description: "Issue ID (issue_id or issue_key required)" }
        issue_key: { type: string, description: "Issue key (issue_id or issue_key required)" }
        comment: { type: string, description: "Comment body" }
        visibility: { type: string, description: "Restrict visibility to a role, e.g. Administrators" }
      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: "Issue ID (issue_id or issue_key required)" }
        issue_key: { type: string, description: "Issue key (issue_id or issue_key required)" }
        limit: { type: number, description: "Maximum comments (default 50)" }
      required: []
    outputs_schema: { properties: {} }
  - id: edit_comment
    name: jira-edit-comment
    description: "Edit an existing comment on an issue."
    inputs_schema:
      properties:
        issue_id: { type: string, description: "Issue ID (issue_id or issue_key required)" }
        issue_key: { type: string, description: "Issue key (issue_id or issue_key required)" }
        comment_id: { type: string, description: "Comment ID" }
        comment: { type: string, description: "New comment body" }
        visibility: { type: string, description: "Restrict visibility to a role" }
      required: [comment_id, comment]
    outputs_schema: { properties: {} }
  - id: delete_comment
    name: jira-delete-comment
    description: "Delete a comment from an issue."
    inputs_schema:
      properties:
        issue_id: { type: string, description: "Issue ID (issue_id or issue_key required)" }
        issue_key: { type: string, description: "Issue key (issue_id or issue_key required)" }
        comment_id: { type: string, description: "Comment ID" }
      required: [comment_id]
    outputs_schema: { properties: {} }

  # ── Links ─────────────────────────────────────────────────────────────────
  - id: add_link
    name: jira-add-link
    description: "Add a remote web link (URL) to an issue."
    inputs_schema:
      properties:
        issue_id: { type: string, description: "Issue ID (issue_id or issue_key required)" }
        issue_key: { type: string, description: "Issue key (issue_id or issue_key required)" }
        url: { type: string, description: "URL to link" }
        title: { type: string, description: "Link title" }
        summary: { type: string, description: "Link summary" }
        relationship: { type: string, description: "Relationship to the issue, e.g. causes" }
      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)."
    inputs_schema:
      properties:
        inward_issue: { type: string, description: "Inward issue key or ID" }
        outward_issue: { type: string, description: "Outward issue key or ID" }
        link_type: { type: string, description: "Link type name, e.g. Blocks, Duplicate" }
        comment: { type: string, description: "Optional comment to add to the inward issue" }
      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)."
    inputs_schema:
      properties:
        issue_id: { type: string, description: "Issue ID (issue_id or issue_key required)" }
        issue_key: { type: string, description: "Issue key (issue_id or issue_key required)" }
        file_name: { type: string, description: "Attachment file name" }
        content_base64: { type: string, description: "File content, base64-encoded" }
      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: "Attachment ID (from jira-get-issue)" }
      required: [attachment_id]
    outputs_schema: { properties: {} }
  - id: delete_attachment
    name: jira-delete-attachment
    description: "Delete an attachment by attachment ID."
    inputs_schema:
      properties:
        attachment_id: { type: string, description: "Attachment ID" }
      required: [attachment_id]
    outputs_schema: { properties: {} }

  # ── Fields / users ────────────────────────────────────────────────────────
  - id: list_fields
    name: jira-list-fields
    description: "List the system and custom issue fields."
    risk: read
    inputs_schema:
      properties:
        limit: { type: number, description: "Maximum fields (default 50)" }
        offset: { type: number, description: "Starting index (default 0)" }
      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: "Display name or email address to search for" }
        max_results: { type: number, description: "Maximum candidates to consider (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: "Account ID (Cloud)" }
        key: { type: string, description: "User key (On-Prem)" }
        username: { type: string, description: "Username (On-Prem)" }
      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: "Return only this board" }
        board_name: { type: string, description: "Filter by board name" }
        project_key_id: { type: string, description: "Filter by project key or ID" }
        type: { type: string, description: "Board type: scrum, kanban or simple" }
        limit: { type: number, description: "Maximum boards (default 50)" }
        offset: { type: number, description: "Starting index (default 0)" }
      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: "Board ID" }
        jql_query: { type: string, description: "JQL to filter the issues" }
        limit: { type: number, description: "Maximum issues (default 50)" }
        offset: { type: number, description: "Starting index (default 0)" }
      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: "Board ID" }
        limit: { type: number, description: "Maximum sprints (default 50)" }
        offset: { type: number, description: "Starting index (default 0)" }
      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: "Sprint ID" }
        jql_query: { type: string, description: "JQL to filter the issues" }
        limit: { type: number, description: "Maximum issues (default 50)" }
        offset: { type: number, description: "Starting index (default 0)" }
      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: "Epic ID (epic_id or epic_key required)" }
        epic_key: { type: string, description: "Epic key (epic_id or epic_key required)" }
        jql_query: { type: string, description: "JQL to filter the issues" }
        limit: { type: number, description: "Maximum issues (default 50)" }
        offset: { type: number, description: "Starting index (default 0)" }
      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"
