jira-api
npx skills add https://github.com/dawiddutoit/custom-claude --skill jira-api
Agent 安装分布
Skill 文档
Jira REST API v3 Documentation
Purpose
This skill provides authoritative guidance on using the Atlassian Jira REST API v3, including endpoint references, authentication methods, request/response formats, query languages, and best practices for programmatic Jira automation and integration.
Quick Start
To get started with the Jira API:
- Authenticate: Use Basic Auth with API token (email:token in base64)
- Make a request:
GET /rest/api/3/issue/{issueIdOrKey} - Parse response: Standard JSON with issue details, changelog, and custom fields
For the project’s JiraClient class:
from jira_tool.client import JiraClient
client = JiraClient()
issue = client.get_issue("PROJ-123")
Instructions
Step 1: Understanding Jira REST API v3 Basics
The Jira REST API v3 is the current standard API for Jira Cloud. Key characteristics:
- Base URL:
https://{jira-instance}.atlassian.net/rest/api/3/ - Authentication: Basic Auth with API tokens (not passwords)
- Data Format: JSON for requests and responses
- Versioning: v3 is the latest; v2 is deprecated
Official Documentation: https://developer.atlassian.com/cloud/jira/platform/rest/v3/
Step 2: Authentication Methods
Basic Auth with API Token (Recommended)
This is what the project uses. Steps:
- Generate API token in Jira user settings (atlassian account)
- Create header:
Authorization: Basic {base64(email:token)} - Add
Accept: application/jsonandContent-Type: application/jsonheaders
from base64 import b64encode
email = "user@example.com"
api_token = "your-api-token"
credentials = f"{email}:{api_token}"
auth_header = b64encode(credentials.encode()).decode()
headers = {
"Authorization": f"Basic {auth_header}",
"Accept": "application/json",
"Content-Type": "application/json",
}
The JiraClient class handles this automatically:
client = JiraClient(
base_url="https://company.atlassian.net",
username="user@example.com",
api_token="token-from-atlassian"
)
OAuth 2.0
For third-party applications:
- Requires OAuth app registration
- More complex but better for user-facing integrations
- See references/reference.md for OAuth flow details
Step 3: Core API Endpoints
Common endpoints you’ll use frequently:
Issue Operations
GET /rest/api/3/issue/{issueIdOrKey}– Get issue detailsPOST /rest/api/3/issues– Create issuePUT /rest/api/3/issue/{issueIdOrKey}– Update issueDELETE /rest/api/3/issue/{issueIdOrKey}– Delete issuePOST /rest/api/3/issue/{issueIdOrKey}/comment– Add comment
Searching & Filtering
GET /rest/api/3/search– Search issues with JQLPOST /rest/api/3/issues/search– Search (alternative POST method)
Projects
GET /rest/api/3/project– List projectsGET /rest/api/3/project/{projectIdOrKey}– Get project details
Users
GET /rest/api/3/users/search– Search usersGET /rest/api/3/user?accountId={id}– Get user detailsGET /rest/api/3/myself– Get current user
Workflows & Transitions
GET /rest/api/3/issue/{issueIdOrKey}/transitions– Get available transitionsPOST /rest/api/3/issue/{issueIdOrKey}/transitions– Transition issue
Custom Fields
GET /rest/api/3/field– List all fields (including custom)GET /rest/api/3/field/search– Search for fields
Webhooks
GET /rest/api/3/webhook– List webhooksPOST /rest/api/3/webhook– Create webhookDELETE /rest/api/3/webhook/{id}– Delete webhook
Step 4: Request Formats and Parameters
Search with JQL (JQL Query Language)
Most powerful way to query issues:
GET /rest/api/3/search?jql=project=PROJ AND status="In Progress"&maxResults=50
JQL Examples:
# Recent issues
project = PROJ AND created >= -7d
# Assigned to me
assignee = currentUser()
# Status workflow
status in (Open, "In Progress") AND updated >= -1d
# Custom fields (need field ID)
customfield_10014 = "Epic Name"
# Text search
summary ~ "bug fix" OR description ~ "critical"
# Complex filtering
(project = PROJ OR project = OTHER)
AND status != Done
AND priority >= High
AND created >= 2024-01-01
JQL Functions:
currentUser()– Current authenticated userendOfDay(),startOfDay()– Date functionsnow()– Current timestampissueFunction()– Advanced scripting
Query Parameters
Common parameters for /search:
jql– JQL query stringstartAt– Pagination start (default 0)maxResults– Items per page (default 50, max 100)fields– Comma-separated field names to returnexpand– Additional data to include (changelog, transitions)orderBy– Sort order (e.g., “created DESC”)
# Using JiraClient
issues = client.search_issues(
jql="project = PROJ AND status = Open",
startAt=0,
maxResults=100,
expand=["changelog"]
)
Create Issue Request
Request body for POST /rest/api/3/issues:
{
"fields": {
"project": {"key": "PROJ"},
"summary": "Issue summary",
"description": {
"type": "doc",
"version": 1,
"content": [
{
"type": "paragraph",
"content": [{"type": "text", "text": "Description text"}]
}
]
},
"issuetype": {"name": "Task"},
"assignee": {"accountId": "user-account-id"},
"priority": {"name": "High"},
"labels": ["bug", "urgent"]
}
}
Update Issue Request
PUT /rest/api/3/issue/{issueKey}:
{
"fields": {
"summary": "Updated summary",
"description": {"type": "doc", "version": 1, "content": []},
"priority": {"name": "Medium"},
"assignee": {"accountId": "new-user-id"}
}
}
Step 5: Expansion and Field Selection
Use expand parameter to include additional data:
GET /rest/api/3/issue/PROJ-123?expand=changelog,transitions
Common expansions:
changelog– Issue change history (required for state duration analysis)transitions– Available workflow transitionseditmeta– Metadata about what fields can be editednames– Human-readable field names
Field Selection – Return only needed fields:
GET /rest/api/3/search?fields=key,summary,status,assignee&maxResults=100
Step 6: Pagination
For large result sets, use pagination:
start_at = 0
max_results = 50
all_issues = []
while True:
issues = client.search_issues(
jql="project = PROJ",
startAt=start_at,
maxResults=max_results
)
all_issues.extend(issues)
if len(issues) < max_results:
break
start_at += max_results
Response includes pagination metadata:
{
"startAt": 0,
"maxResults": 50,
"total": 523,
"isLast": false,
"values": [...]
}
Step 7: Atlassian Document Format (ADF)
Rich text content (descriptions, comments) uses ADF. The project’s JiraDocumentBuilder simplifies this:
from jira_tool.formatter import JiraDocumentBuilder
doc = JiraDocumentBuilder()
doc.add_heading("Title", level=1)
doc.add_paragraph(doc.bold("Key"), doc.add_text(": "), doc.add_text("Value"))
doc.add_bullet_list(["Item 1", "Item 2"])
doc.add_code_block("code content", language="python")
adf = doc.build() # Returns ADF dict for API
ADF Structure:
{
"type": "doc",
"version": 1,
"content": [
{
"type": "heading",
"attrs": {"level": 1},
"content": [{"type": "text", "text": "Heading"}]
},
{
"type": "paragraph",
"content": [{"type": "text", "text": "Paragraph"}]
}
]
}
Common ADF nodes:
heading– Headings (levels 1-6)paragraph– Text paragraphsbulletList/orderedList– ListscodeBlock– Code blockspanel– Info panels (info, note, warning, success, error)blockquote– Block quotestable– Tables
See references/reference.md for comprehensive ADF examples.
Step 8: Error Handling and Status Codes
Common HTTP status codes:
| Code | Meaning | Handling |
|---|---|---|
| 200 | Success | Parse response normally |
| 201 | Created | Resource created successfully |
| 204 | No Content | Successful but empty response |
| 400 | Bad Request | Check request format/parameters |
| 401 | Unauthorized | Check authentication credentials |
| 403 | Forbidden | Check permissions |
| 404 | Not Found | Issue/resource doesn’t exist |
| 429 | Too Many Requests | Rate limited – implement backoff |
| 500 | Server Error | Retry with exponential backoff |
Error Response Format:
{
"errorMessages": ["Error message"],
"errors": {
"fieldName": "Field-specific error"
}
}
The JiraClient includes automatic retry logic for 429, 500, 502, 503, 504:
client = JiraClient(max_retries=3) # Automatic exponential backoff
Step 9: Rate Limiting
Jira Cloud has rate limits:
- Anonymous requests: Limited
- Authenticated: Higher limits (typically 10 requests/second)
- Header:
X-RateLimit-*headers in response
Check rate limit headers:
response = client.session.get(url)
print(response.headers.get('X-RateLimit-Limit'))
print(response.headers.get('X-RateLimit-Remaining'))
print(response.headers.get('X-RateLimit-Reset'))
Best practices:
- Use
maxResults=100in searches (fewer requests) - Cache results when possible
- Implement exponential backoff on 429 (the client does this)
- Batch operations when possible
Step 10: Custom Fields
Custom fields have IDs (e.g., customfield_10014). They vary per instance.
Discover custom fields:
GET /rest/api/3/field
# Using JiraClient
fields = client.list_fields()
epic_field = client.get_epic_link_field() # Auto-discovers common field IDs
Use in queries and updates:
# In JQL
GET /rest/api/3/search?jql=customfield_10014="Epic Name"
# In updates
PUT /rest/api/3/issue/PROJ-123
{
"fields": {
"customfield_10014": "Epic Name"
}
}
Step 11: Common Patterns and Recipes
Create Issue Under Epic:
from jira_tool.formatter import JiraDocumentBuilder
doc = JiraDocumentBuilder()
doc.add_paragraph(doc.add_text("Issue description"))
adf = doc.build()
issue_data = {
"fields": {
"project": {"key": "PROJ"},
"summary": "New issue",
"description": adf,
"issuetype": {"name": "Task"},
"customfield_10014": "PROJ-1" # Epic Link field
}
}
response = client.create_issue(issue_data)
Bulk Update Issues:
# Get issues
issues = client.search_issues(
jql="project = PROJ AND status = Open",
maxResults=100
)
# Update each
for issue in issues:
client.update_issue(
issue["key"],
{"fields": {"priority": {"name": "High"}}}
)
Transition Workflow:
# Get available transitions
transitions = client.get_transitions("PROJ-123")
# Find the transition ID you want
for transition in transitions:
if transition["name"] == "Done":
transition_id = transition["id"]
break
# Execute transition
client.transition_issue("PROJ-123", transition_id)
Search and Export:
from jira_tool.analysis.formatters import format_as_csv
issues = client.search_issues(
jql="project = PROJ AND created >= -7d",
expand=["changelog"]
)
csv_output = format_as_csv(issues)
print(csv_output)
Examples
Example 1: Simple API Call – Get Issue
Using Jira REST API directly:
curl -X GET \
"https://company.atlassian.net/rest/api/3/issue/PROJ-123" \
-H "Authorization: Basic $(echo -n 'email:token' | base64)" \
-H "Accept: application/json"
Using the project’s client:
from jira_tool.client import JiraClient
client = JiraClient()
issue = client.get_issue("PROJ-123")
print(f"Summary: {issue['fields']['summary']}")
print(f"Status: {issue['fields']['status']['name']}")
Example 2: Advanced Search with JQL
Find all open bugs assigned to current user:
curl -X GET \
"https://company.atlassian.net/rest/api/3/search" \
-G --data-urlencode 'jql=project=PROJ AND type=Bug AND assignee=currentUser() AND status != Done' \
-G --data-urlencode 'maxResults=100' \
-G --data-urlencode 'expand=changelog' \
-H "Authorization: Basic ..." \
-H "Accept: application/json"
Using the client:
from jira_tool.client import JiraClient
client = JiraClient()
issues = client.search_issues(
jql='project = PROJ AND type = Bug AND assignee = currentUser() AND status != Done',
maxResults=100,
expand=['changelog']
)
for issue in issues:
print(f"{issue['key']}: {issue['fields']['summary']}")
Example 3: Create Issue with Rich Content
Create a detailed issue with formatted description:
from jira_tool.client import JiraClient
from jira_tool.formatter import JiraDocumentBuilder
client = JiraClient()
# Build rich content
doc = JiraDocumentBuilder()
doc.add_heading("Issue Description", level=1)
doc.add_paragraph(doc.add_text("Background: "), doc.add_text("Detailed background"))
doc.add_heading("Steps to Reproduce", level=2)
doc.add_bullet_list([
"Step 1",
"Step 2",
"Step 3"
])
doc.add_panel("error", doc.add_paragraph(doc.add_text("Expected error")))
adf_description = doc.build()
# Create issue
response = client.create_issue({
"fields": {
"project": {"key": "PROJ"},
"summary": "Bug: Application crashes on login",
"description": adf_description,
"issuetype": {"name": "Bug"},
"priority": {"name": "Highest"},
"labels": ["critical", "regression"]
}
})
print(f"Created issue: {response['key']}")
Example 4: Analyze Issue State Durations
Use the project’s state analyzer to track time in workflow states:
from jira_tool.client import JiraClient
from jira_tool.analysis.state_analyzer import StateDurationAnalyzer
client = JiraClient()
# Search with changelog
issues = client.search_issues(
jql="project = PROJ AND created >= -30d",
expand=["changelog"]
)
# Analyze state transitions
analyzer = StateDurationAnalyzer()
durations = analyzer.analyze_issues(issues)
# Export results
csv_output = analyzer.format_as_csv(durations)
print(csv_output)
Example 5: Handle Pagination
Efficiently fetch large result sets:
from jira_tool.client import JiraClient
client = JiraClient()
start_at = 0
max_results = 50
total_fetched = 0
all_issues = []
while True:
issues = client.search_issues(
jql="project = PROJ",
startAt=start_at,
maxResults=max_results
)
all_issues.extend(issues)
total_fetched += len(issues)
# Check if we got fewer results than requested (last page)
if len(issues) < max_results:
break
start_at += max_results
print(f"Fetched {total_fetched} issues...")
print(f"Total issues: {total_fetched}")
Requirements
- Python 3.8 or higher
requestslibrary (included in project)- Valid Jira Cloud instance with REST API v3 access
- API token generated from Atlassian account settings
- Environment variables:
JIRA_BASE_URL,JIRA_USERNAME,JIRA_API_TOKEN
See Also
- reference.md – Comprehensive API reference, ADF node types, webhook payloads, and field mappings
- examples.md – Extended examples for complex scenarios, batch operations, and error handling
- Official Jira API Docs: https://developer.atlassian.com/cloud/jira/platform/rest/v3/
- Project JiraClient source:
src/jira_tool/client.py - Project ADF Builder:
src/jira_tool/formatter.py