---
title: "How to Integrate with Atlassian Jira"
img: https://s3.us-east-2.amazonaws.com/unified-article-images/how_to_integrate_with_atlassian_jira-icon.png
date: 2026-02-26T00:39:00.000Z
tag: Product
description: "A Jira API integration lets your product create issues, sync project data, update comments, transition workflows, and pull structured task data from the system..."
url: "https://unified.to/blog/how_to_integrate_with_atlassian_jira"
---

# How to Integrate with Atlassian Jira
------
_February 26, 2026_

A **Jira API integration** lets your product create issues, sync project data, update comments, transition workflows, and pull structured task data from the system many engineering, product, and IT teams already use to run work.


For B2B SaaS teams, that usually means pushing tickets into Jira from your app, syncing issue status back into your product, surfacing project or sprint context, or automating workflows that would otherwise require manual updates.


But a production-grade Jira integration involves more than sending a request to create an issue. You need to choose the right authentication model, understand Jira Cloud's REST patterns, handle JQL search and [pagination](/docs/reference/pagination) correctly, design around [rate limits](/docs/reference/rate_limits#rate-limits), and use [webhooks](/blog/replacing_polling_with_unified_webhooks_and_virtual_streams) where real-time updates matter.


This guide covers:

1. How to integrate directly with the Jira API
2. The operational complexity you will own
3. How to integrate Jira using Unified's Task, HRIS, Metadata, and Storage APIs
4. When to build direct vs use an integration layer

## Direct Jira API integration


### Step 1: Choose the right authentication model


For Jira Cloud, there are two practical paths for most developers.


For simple internal scripts or one-off admin tooling, **basic authentication with an Atlassian API token** is still the fastest option. Atlassian documents creating an API token, combining it with the user's email, and sending it in the `Authorization: Basic` header for REST requests. [Manage API tokens](https://support.atlassian.com/atlassian-account/docs/manage-api-tokens-for-your-atlassian-account/) and [Basic auth for REST APIs](https://developer.atlassian.com/cloud/jira/platform/basic-auth-for-rest-apis/)


For customer-facing SaaS integrations, **OAuth 2.0 (3LO)** is the better fit. It lets users authorize your app without sharing credentials, supports scoped access, and is the right pattern when each customer connects their own Jira tenant. Atlassian documents the 3LO flow in its OAuth guide. [OAuth 2.0 (3LO) apps](https://developer.atlassian.com/cloud/jira/platform/oauth-2-3lo-apps/)


In practice:

- Use **API token auth** for internal tools and testing
- Use **OAuth 2.0 (3LO)** for multi-user SaaS integrations
- Use **refresh tokens** when you need long-lived delegated access

### Step 2: Register an OAuth 2.0 app if you are building a SaaS integration


In the [Atlassian developer console](https://developer.atlassian.com/console/myapps/), create an app, configure OAuth 2.0, and add your callback URL. Atlassian's flow uses an authorization endpoint at `https://auth.atlassian.com/authorize`, followed by token exchange at `https://auth.atlassian.com/oauth/token`. [OAuth 2.0 (3LO) apps](https://developer.atlassian.com/cloud/jira/platform/oauth-2-3lo-apps/)


The authorization request includes:

- `audience=api.atlassian.com`
- `client_id`
- `scope`
- `redirect_uri`
- `state`
- `response_type=code`
- `prompt=consent`

After the user approves access, Atlassian redirects back with a `code`. Your backend exchanges that code for an access token and, if requested, a refresh token.


A direct token exchange looks like this:


```json
POST https://auth.atlassian.com/oauth/token
{
  "grant_type": "authorization_code",
  "client_id": "YOUR_CLIENT_ID",
  "client_secret": "YOUR_CLIENT_SECRET",
  "code": "AUTHORIZATION_CODE",
  "redirect_uri": "YOUR_REDIRECT_URI"
}
```


### Step 3: Resolve the tenant before calling Jira APIs


With Atlassian OAuth, getting an access token is not the final step. You also need the site's `cloudId` so you can address the correct Jira tenant.


Atlassian documents calling the accessible resources endpoint:


```plain text
GET https://api.atlassian.com/oauth/token/accessible-resources
Authorization: Bearer ACCESS_TOKEN
```


Then you call Jira REST APIs using this pattern:


```plain text
https://api.atlassian.com/ex/jira/{cloudId}/rest/api/3/...
```


That tenant-resolution step is easy to miss if you are used to simpler OAuth flows. [OAuth 2.0 (3LO) apps](https://developer.atlassian.com/cloud/jira/platform/oauth-2-3lo-apps/)


### Step 4: Understand the core Jira REST model


Jira Cloud's main public API surface is **REST**, not GraphQL. Atlassian's platform docs position the Jira Cloud REST API as the core path for reading and writing issues, projects, users, comments, workflows, and related resources. [Jira Cloud platform REST API](https://developer.atlassian.com/cloud/jira/platform/rest/v3/intro/)


At a minimum, most integrations work with:

- Issues
- Projects
- Comments
- Users
- Transitions
- Search via JQL
- Webhooks

If your use case is customer support escalation, bug tracking, incident management, or product planning sync, these are the objects that matter first.


## Core Jira operations


### Create an issue


Creating issues is usually the first meaningful integration step. In Jira Cloud, the endpoint is:


```plain text
POST /rest/api/3/issue
```


A minimal request body includes fields like project, issue type, and summary:


```json
{
  "fields": {
    "project": { "key": "PROJ" },
    "issuetype": { "name": "Bug" },
    "summary": "Something is broken",
    "description": {
      "type": "doc",
      "version": 1,
      "content": [
        {
          "type": "paragraph",
          "content": [
            { "type": "text", "text": "Detailed bug report goes here." }
          ]
        }
      ]
    }
  }
}
```


Atlassian's current Jira Cloud examples use **Atlassian Document Format** for rich-text fields like description and comment bodies, so you should not assume plain strings everywhere. [Issues API group](https://developer.atlassian.com/cloud/jira/platform/rest/v3/api-group-issues/)


### Get an issue


To retrieve a single issue:


```plain text
GET /rest/api/3/issue/{issueIdOrKey}
```


You can also control the response with parameters like `fields` and `expand` to avoid over-fetching. [Get issue](https://developer.atlassian.com/cloud/jira/platform/rest/v3/api-group-issues/#api-rest-api-3-issue-issueidorkey-get)


### Update an issue


To update an issue:


```plain text
PUT /rest/api/3/issue/{issueIdOrKey}
```


You send only the fields you want to change. A typical example is updating summary, description, assignee, due date, or custom fields. [Edit issue](https://developer.atlassian.com/cloud/jira/platform/rest/v3/api-group-issues/#api-rest-api-3-issue-issueidorkey-put)


### Delete an issue


To delete an issue:


```plain text
DELETE /rest/api/3/issue/{issueIdOrKey}
```


This should be used carefully, especially in customer-facing integrations, because deleting records can break auditability and downstream sync assumptions. [Delete issue](https://developer.atlassian.com/cloud/jira/platform/rest/v3/api-group-issues/#api-rest-api-3-issue-issueidorkey-delete)


### Search issues with JQL


For real integrations, direct issue CRUD is only part of the story. Search matters just as much.


Jira's query language, **JQL**, is how you find issues by project, status, assignee, priority, labels, custom fields, and date conditions. Atlassian documents the current search endpoints under the Jira REST API. [Issue search](https://developer.atlassian.com/cloud/jira/platform/rest/v3/api-group-issue-search/)


A typical search request looks like:


```plain text
GET /rest/api/3/search/jql?jql=project=PROJ AND status="In Progress"
```


Or via POST when the query is larger or more structured:


```json
POST /rest/api/3/search/jql
{
  "jql": "project = PROJ AND assignee = currentUser()",
  "maxResults": 50,
  "fields": ["summary", "status", "assignee"]
}
```


If your integration needs to mirror tickets into your product, generate dashboards, or filter subsets of work, JQL is a core part of the design.


### Add and read comments


Comments are another common integration surface, especially in support and collaboration workflows.


To add a comment:


```plain text
POST /rest/api/3/issue/{issueIdOrKey}/comment
```


Like descriptions, comment bodies use Atlassian Document Format. [Issue comments](https://developer.atlassian.com/cloud/jira/platform/rest/v3/api-group-issue-comments/)


To list comments:


```plain text
GET /rest/api/3/issue/{issueIdOrKey}/comment
```


### Read transitions and move issues through a workflow


Jira workflows are not just labels. They govern how work moves through statuses.


To get the available transitions for an issue:


```plain text
GET /rest/api/3/issue/{issueIdOrKey}/transitions
```


To perform a transition:


```plain text
POST /rest/api/3/issue/{issueIdOrKey}/transitions
```


With a body like:


```json
{
  "transition": {
    "id": "31"
  }
}
```


This is the API pattern you use when your product needs to move work to 'In Progress,' 'In Review,' or 'Done' based on events in your own system. [Issue transitions](https://developer.atlassian.com/cloud/jira/platform/rest/v3/api-group-issues/#api-rest-api-3-issue-issueidorkey-transitions-get)


### List projects


Projects are another foundational object. To search or list projects:


```plain text
GET /rest/api/3/project/search
```


This is useful for setup flows where you need customers to pick a Jira project before issue creation begins. [Project API group](https://developer.atlassian.com/cloud/jira/platform/rest/v3/api-group-projects/)


## Pagination, rate limits, and webhooks


### Pagination


Jira REST responses commonly include:

- `startAt`
- `maxResults`
- `total`
- `isLast`

You page through large result sets by adjusting `startAt` and `maxResults`. Atlassian documents this pagination model across Jira Cloud REST resources. [Expansion, pagination, and ordering](https://developer.atlassian.com/cloud/jira/platform/rest/v3/intro/#expansion)


For some newer search flows, Atlassian also uses `nextPageToken`, so you should design your client code to follow the response shape of the specific endpoint rather than assuming a single pagination style everywhere.


### Rate limits


Jira Cloud enforces rate limits, and Atlassian documents both burst-based and resource-specific limits. [Rate limiting](https://developer.atlassian.com/cloud/jira/platform/rate-limiting/)


Important practical points:

- `429 Too Many Requests` is the signal that you hit a limit
- The `Retry-After` header tells you how long to wait
- Atlassian may also return headers like `X-RateLimit-Limit` and `X-RateLimit-Remaining`
- Some write-heavy patterns are additionally constrained on a per-issue basis

For production integrations, you should:

- respect `Retry-After`
- implement exponential backoff
- avoid bursty sync behavior
- reduce unnecessary polling
- batch work where appropriate

### Webhooks


Webhooks are the right model when you need near real-time changes without constant polling.


Atlassian documents Jira Cloud webhooks for issue, comment, project, sprint, version, and related events. [Webhooks](https://developer.atlassian.com/cloud/jira/platform/webhooks/)


Important webhook behavior:

- webhook deliveries are sent to your HTTPS endpoint
- failures are retried
- duplicate deliveries are possible
- ordering is not guaranteed
- you should treat deliveries as idempotent
- you should secure your endpoint and validate origin/signature details where applicable

For most SaaS products, the key reason to use webhooks is simple: they let you update your system when Jira changes, instead of repeatedly asking Jira whether anything changed.


## Where Jira integrations become operationally complex


A direct Jira integration means you own:

- API token vs OAuth architecture
- multi-tenant OAuth flows
- token refresh and secure storage
- tenant discovery via `cloudId`
- issue field mapping
- custom field handling
- JQL query construction
- pagination logic
- retry and throttling controls
- webhook registration and delivery handling
- workflow transition logic
- project and user sync behavior

For a simple internal script, this is manageable.


For a multi-tenant B2B SaaS product supporting many customer Jira instances, this becomes real infrastructure.


The complexity tends to show up in three places:


First, **field variability**. Jira issue types, custom fields, statuses, and workflow transitions differ across tenants.


Second, **auth and tenancy**. OAuth in Atlassian Cloud is not just 'get a token and call `/me`.' You need to resolve accessible resources and persist tenant context.


Third, **operational reliability**. Search, webhooks, retries, backfills, and partial failure handling all become part of the integration surface.


## Integrating Jira via Unified


Unified gives you a normalized Jira path across Task, HRIS, Storage, Metadata, and Passthrough APIs.


From the Jira integration details you shared, Unified supports objects such as:

- `task` for Jira issues
- `task_project` for Jira projects
- `task_comment` for issue comments
- `task_change` for issue changelog/change history
- `storage_file` for attachments
- `hris_group` for Jira groups
- `hris_employee` for users
- `passthrough` for unsupported Jira-specific endpoints

### Step 1: Register Jira OAuth for Unified


When integrating Jira through Unified, set the callback URL to:


```plain text
https://api.unified.to/oauth/code
```


Unified's Jira integration page also indicates OAuth 2.0 credentials should be registered through the Atlassian developer console. [Atlassian developer console](https://developer.atlassian.com/console/myapps/) and [Unified guide to registering an Atlassian developer app](https://unified.to/blog/how_to_register_an_atlassian_developer_app_and_get_oauth2_credentials)


Unified scope mappings for Jira, based on the integration details you shared, include examples like:

- `task_task_read` → `offline_access, read:jira-work, read:jira-user`
- `task_task_write` → `offline_access, read:jira-work, read:jira-user, write:jira-work`
- `task_project_read` → `offline_access, read:jira-user, read:jira-work`
- `task_project_write` → `offline_access, read:jira-user, manage:jira-configuration, manage:jira-project, write:project:jira`
- `task_comment_read` → `offline_access, read:jira-user, read:jira-work`
- `task_comment_write` → `offline_access, read:jira-user, write:jira-work`
- `webhook` → `offline_access, read:jira-work, manage:jira-webhook`

That gives you a much cleaner way to request what you need without hand-building Atlassian scope combinations from scratch.


### Step 2: Authorize a Jira connection


With Unified, the end result of authorization is a `connection_id`. That connection represents one customer's Jira account and is what you use in future API calls.


This is materially simpler than managing raw tenant auth and provider-specific request setup yourself.


### Step 3: Use Unified's normalized task APIs


List projects:


```plain text
GET /task/{connection_id}/project
```


List tasks:


```plain text
GET /task/{connection_id}/task
```


Get a task:


```plain text
GET /task/{connection_id}/task/{id}
```


Create a task:


```plain text
POST /task/{connection_id}/task
```


Update a task:


```plain text
PUT /task/{connection_id}/task/{id}
```


Remove a task:


```plain text
DELETE /task/{connection_id}/task/{id}
```


From the coverage you shared, Unified's Jira task object includes readable fields like:

- `id`
- `name`
- `project_id`
- `status`
- `priority`
- `tags`
- `assigned_user_ids`
- `creator_user_id`
- `due_at`
- `updated_at`
- `url`

And writable fields include practical ones such as:

- `name`
- `project_id`
- `assigned_user_ids`
- `completed_at`
- `due_at`
- `notes`
- `parent_id`
- `tags`

That means the common issue creation and update path can be handled through one normalized object instead of raw Jira field payloads.


### Step 4: Use Unified comments, users, groups, and change history


Unified's Jira coverage also gives you:

- `task_comment` for issue comments
- `task_change` for issue changelog/history
- `hris_employee` for Jira users
- `hris_group` for Jira groups
- `storage_file` for attachments

That is useful when your app needs not just issue creation, but also:

- comment sync
- user assignment flows
- audit/change tracking
- attachment retrieval
- group-aware routing or permissions logic

### Step 5: Use Unified webhooks


From the Jira integration details you shared:

- `task` supports **virtual and native webhooks**
- `task_comment` supports **native webhooks**
- other objects vary in support

This matters because it means you can receive issue and comment updates without building the raw Jira webhook management flow yourself.


Unified's virtual webhook model also gives you fallback coverage in cases where native event behavior is limited or inconsistent across providers.


### Step 6: Use passthrough for Jira-specific gaps


Jira is broad. Boards, sprints, agile endpoints, specialized metadata, or tenant-specific features may not always fit perfectly into normalized models.


That is why passthrough matters.


Unified's Jira integration supports passthrough methods:

- `GET`
- `POST`
- `PUT`
- `DELETE`

So if you need a Jira-specific endpoint not yet normalized, you can still call it through Unified while keeping the same auth and connection model.


## Direct vs Unified: when to choose each


### Build directly with Jira if:

- Jira is a strategic core integration for your product
- you need deep Jira-specific behavior, including custom field logic and workflow nuance
- you need direct control over Atlassian auth, tenant routing, and webhook handling
- you are comfortable owning Jira's operational complexity long term

### Use Unified if:

- Jira is one of several task or work-management integrations on your roadmap
- your core use cases are issues, projects, comments, users, groups, and attachments
- you want normalized APIs and simpler auth flows
- you want built-in webhook coverage where possible
- you still want passthrough for provider-specific gaps

## Final thoughts


A Jira API integration looks straightforward at the first-request level.


The real complexity shows up in:

- choosing the right auth model
- handling tenant resolution correctly
- working with Jira's field and workflow variability
- using search and pagination efficiently
- dealing with rate limits
- keeping data in sync through webhooks

If Jira is central to your product, building directly can make sense. You get maximum flexibility and full access to the platform's native model.


If Jira is one integration among many, a normalized integration layer can reduce engineering drag, shorten time to launch, and still leave you an escape hatch for Jira-specific endpoints when you need them.


[Start your 30-day free trial](https://app.unified.to/login)


[Book a demo](https://calendly.com/d/cph9-g8n-jzg/connect-with-unified)