name: atlassian-api description: Integrates with Atlassian suite (Jira, Confluence, Bitbucket, Rovo, Forge) using atlassian-python-api to automate issue tracking, documentation, and code management. license: MIT compatibility: opencode metadata: version: "1.0.0" domain: coding triggers: jira api, confluence api, bitbucket api, atlassian python, jira automation, confluence pages, atlassian forge archetypes:
- tactical
- generation anti_triggers:
- brainstorming
- vague ideation
- code golf
- over-engineering response_profile: verbosity: low directive_strength: high abstraction_level: operational role: implementation scope: implementation output-format: code content-types:
- code
- guidance
- do-dont
- examples related-skills: coding-microsoft-graph-api, coding-google-workspace-api, coding-asana-api
Atlassian API Integration
Integrates with the Atlassian ecosystem — Jira (issue tracking), Confluence (documentation), Bitbucket (Git hosting), Rovo (AI search), and Forge (serverless apps) — using the atlassian-python-api library to automate development workflows across planning, coding, and documentation.
TL;DR for Code Generation
- Use
Jira,Confluence, andBitbucketclasses fromatlassian-python-apiv4.0+ - Authenticate with personal access tokens (cloud) or basic auth (server) — prefer PATs in production
- Jira uses JQL for issue queries; Confluence uses CQL for content search
- Paginate with
start/limitparameters (Jira) orcursor-based paging (Confluence cloud) - Use Jira's
updatemethod with field dicts for partial updates — avoid full object replacement - Handle
requests.exceptions.HTTPErrorfor API errors, differentiate 401 (auth), 403 (permissions), 404 (not found)
When to Use
Use this skill when:
- Creating, querying, updating, or transitioning Jira issues
- Fetching and updating Confluence pages, attachments, and spaces
- Automating Bitbucket pull request reviews and repository management
- Building Forge apps that extend Atlassian products
- Synchronizing Jira issues with external databases or spreadsheets
- Generating release notes from Jira issues and Confluence pages
- Cross-referencing code commits (Bitbucket) with tickets (Jira) in automated pipelines
When NOT to Use
- Real-time issue updates (use Jira webhooks or Forge event handlers instead of polling)
- Bulk-importing thousands of issues in a single request — batch in chunks of 50
- Replacing Bitbucket Pipelines for CI/CD (use the pipeline API only for triggers/status)
- Accessing Jira Server with expired certificates or unsupported TLS versions
Core Workflow
Choose Authentication Method — For Jira Cloud, generate an API token from
https://id.atlassian.com/manage/api-tokens. For Confluence Cloud, use the same token with the appropriate subdomain. Checkpoint: Verify the token works withjira.get_user().Initialize the Client — Instantiate
Jira(url=..., token=...)orConfluence(url=..., token=...). Always specify the full instance URL (e.g.,https://your-domain.atlassian.net). Checkpoint: Calljira.get_project(project_key)to validate connectivity.Construct JQL or CQL Query — For Jira, build a JQL string with project, status, assignee, and date filters. For Confluence, use CQL for space/page filtering. Checkpoint: Run the query with
limit=1first to confirm syntax.Execute and Paginate — Use
jira.jql(query, start=0, limit=50)and check thetotalfield. Incrementstartbylimituntil all results are consumed. Checkpoint: Verify theissueslist is non-empty and contains expected fields.Process Results — Iterate over issues or pages. Map
fieldsto your data model. For updates, usejira.update_issue_field(issue_key, fields=...)with a dict of only changed fields. Checkpoint: Confirm the update by re-fetching the issue.Handle Errors — Wrap API calls in
try/except requests.exceptions.HTTPError. For 429 (rate limit), parseRetry-Afterheader and wait. For 401, refresh credentials. Checkpoint: Log structured error output with request ID.
Implementation Patterns
Pattern 1: Query Jira Issues with JQL
import os
from atlassian import Jira
jira = Jira(
url="https://your-domain.atlassian.net",
token=os.environ["ATLASSIAN_API_TOKEN"],
)
def find_open_bugs(project_key: str, max_results: int = 100) -> list[dict]:
"""Find open bug tickets in a project, ordered by priority."""
jql = (
f'project = "{project_key}" '
f'AND issuetype = Bug '
f'AND status NOT IN (Done, Closed, Resolved) '
f'ORDER BY priority DESC, created ASC'
)
issues = []
start = 0
while True:
results = jira.jql(jql, start=start, limit=50)
issues.extend(results.get("issues", []))
if start + 50 >= results.get("total", 0):
break
start += 50
return issues
bugs = find_open_bugs("PROJ")
for issue in bugs:
key = issue["key"]
summary = issue["fields"]["summary"]
priority = issue["fields"]["priority"]["name"]
print(f"[{priority}] {key}: {summary}")
Pattern 2: Create a Confluence Page
from atlassian import Confluence
confluence = Confluence(
url="https://your-domain.atlassian.net/wiki",
token=os.environ["ATLASSIAN_API_TOKEN"],
)
def publish_release_notes(space_key: str, parent_id: int | None, title: str, body_html: str) -> dict:
"""Create or update a Confluence page with release notes."""
status = confluence.create_page(
space=space_key,
title=title,
body=body_html,
parent_id=parent_id,
representation="storage",
)
return status
# HTML body in Confluence Storage Format
html_content = (
"<h1>Release v2.5.0</h1>"
"<ul><li>PROJ-123: Fixed login timeout</li>"
"<li>PROJ-456: Added export feature</li></ul>"
)
publish_release_notes("ENG", 987654, "Release v2.5.0 Notes", html_content)
Pattern 3: Transition a Jira Issue
def transition_issue(issue_key: str, target_status: str) -> bool:
"""Transition a Jira issue to the target status by resolution name."""
transitions = jira.get_transitions(issue_key)
target_id = None
for t in transitions.get("transitions", []):
if t["name"].lower() == target_status.lower():
target_id = t["id"]
break
if not target_id:
print(f"Transition '{target_status}' not available for {issue_key}")
return False
jira.transition_issue(issue_key, target_id)
return True
transition_issue("PROJ-789", "In Review")
Pattern 4: BAD vs GOOD — Updating Issues
# ❌ BAD — fetches entire issue only to update one field
issue = jira.get_issue("PROJ-123")
issue["fields"]["description"] = "Updated description"
jira.update_issue("PROJ-123", issue)
# ✅ GOOD — targeted field update, minimal payload
jira.update_issue_field("PROJ-123", fields={"description": "Updated description"})
Pattern 5: BAD vs GOOD — Pagination
# ❌ BAD — no pagination, assumes all results fit in one page
results = jira.jql("project = PROJ ORDER BY key")
all_issues = results["issues"]
# ✅ GOOD — paginated loop respecting total count
def get_all_issues(jql_query: str, batch_size: int = 50) -> list[dict]:
"""Paginate through all JQL results."""
collected = []
start = 0
while True:
page = jira.jql(jql_query, start=start, limit=batch_size)
collected.extend(page.get("issues", []))
total = page.get("total", 0)
if start + batch_size >= total:
break
start += batch_size
return collected
Constraints
MUST DO
- Use API tokens over passwords for all Atlassian Cloud instances
- Always paginate JQL and CQL results with
start/limitor cursor-based paging - Use
jira.update_issue_field()for partial updates — never send full issue objects - Handle 429 rate limits with exponential backoff and
Retry-Afterheader parsing
MUST NOT DO
- Commit API tokens to version control — use environment variables or secrets manager
- Assume Jira issue IDs are sequential or predictable
- Use Confluence Storage Format for simple content (use
representation="wiki"instead) - Run JQL queries without a
projectfilter — can scan entire instance
Output Template
Every integration function should expose:
- Authentication —
Jira(url, token)orConfluence(url, token)instantiation - Query/Command — JQL/CQL string or targeted method call
- Pagination — Loop with
start/limitfor Jira or cursor for Confluence cloud - Data Mapping — Extract fields from Atlassian response dicts to your domain model
- Error Handling —
try/except requests.exceptions.HTTPErrorwith status-specific recovery
Related Skills
| Skill | Purpose | |