Claude-skill-registry jira-api
git clone https://github.com/majiayu000/claude-skill-registry
T=$(mktemp -d) && git clone --depth=1 https://github.com/majiayu000/claude-skill-registry "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/data/jira-api" ~/.claude/skills/majiayu000-claude-skill-registry-jira-api && rm -rf "$T"
skills/data/jira-api/SKILL.mdJira 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
andAccept: application/json
headersContent-Type: application/json
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 issue detailsGET /rest/api/3/issue/{issueIdOrKey}
- Create issuePOST /rest/api/3/issues
- Update issuePUT /rest/api/3/issue/{issueIdOrKey}
- Delete issueDELETE /rest/api/3/issue/{issueIdOrKey}
- Add commentPOST /rest/api/3/issue/{issueIdOrKey}/comment
Searching & Filtering
- Search issues with JQLGET /rest/api/3/search
- Search (alternative POST method)POST /rest/api/3/issues/search
Projects
- List projectsGET /rest/api/3/project
- Get project detailsGET /rest/api/3/project/{projectIdOrKey}
Users
- Search usersGET /rest/api/3/users/search
- Get user detailsGET /rest/api/3/user?accountId={id}
- Get current userGET /rest/api/3/myself
Workflows & Transitions
- Get available transitionsGET /rest/api/3/issue/{issueIdOrKey}/transitions
- Transition issuePOST /rest/api/3/issue/{issueIdOrKey}/transitions
Custom Fields
- List all fields (including custom)GET /rest/api/3/field
- Search for fieldsGET /rest/api/3/field/search
Webhooks
- List webhooksGET /rest/api/3/webhook
- Create webhookPOST /rest/api/3/webhook
- Delete webhookDELETE /rest/api/3/webhook/{id}
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:
- Current authenticated usercurrentUser()
,endOfDay()
- Date functionsstartOfDay()
- Current timestampnow()
- Advanced scriptingissueFunction()
Query Parameters
Common parameters for
/search:
- JQL query stringjql
- Pagination start (default 0)startAt
- Items per page (default 50, max 100)maxResults
- Comma-separated field names to returnfields
- Additional data to include (changelog, transitions)expand
- Sort order (e.g., "created DESC")orderBy
# 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:
- Issue change history (required for state duration analysis)changelog
- Available workflow transitionstransitions
- Metadata about what fields can be editededitmeta
- Human-readable field namesnames
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:
- Headings (levels 1-6)heading
- Text paragraphsparagraph
/bulletList
- ListsorderedList
- Code blockscodeBlock
- Info panels (info, note, warning, success, error)panel
- Block quotesblockquote
- Tablestable
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:
headers in responseX-RateLimit-*
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
in searches (fewer requests)maxResults=100 - 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
library (included in project)requests- Valid Jira Cloud instance with REST API v3 access
- API token generated from Atlassian account settings
- Environment variables:
,JIRA_BASE_URL
,JIRA_USERNAMEJIRA_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