By Sagar Shankaran, Founder of CallSphere
Build an AI agent that integrates with Jira for automated ticket creation, intelligent updates, JQL-powered queries, and sprint management using the Jira REST API with practical Python examples.
Key takeaways
Jira is the backbone of project tracking for software teams. An AI agent connected to Jira can automate ticket creation from Slack messages or emails, enrich tickets with context from codebases, estimate story points based on historical data, manage sprint planning, and generate sprint retrospective summaries — turning Jira from a manual data entry system into an intelligent project assistant.
Use API tokens for Jira Cloud authentication. The REST API provides comprehensive access to issues, boards, sprints, and workflows.
flowchart LR
USER(["Customer"])
CHANNEL{"Channel"}
CHAT["Chat agent"]
VOICE["Voice agent"]
EMAIL["Email agent"]
TRIAGE["Triage and<br/>intent detection"]
KB[("Knowledge base<br/>RAG")]
CRM[("CRM context")]
AUTORES{"Auto resolvable?"}
RESOLVE(["Resolved with<br/>cited answer"])
HUMAN(["Tier 2 agent"])
USER --> CHANNEL --> CHAT --> TRIAGE
CHANNEL --> VOICE --> TRIAGE
CHANNEL --> EMAIL --> TRIAGE
TRIAGE --> KB
TRIAGE --> CRM
TRIAGE --> AUTORES
AUTORES -->|Yes| RESOLVE
AUTORES -->|No| HUMAN
style TRIAGE fill:#4f46e5,stroke:#4338ca,color:#fff
style AUTORES fill:#f59e0b,stroke:#d97706,color:#1f2937
style RESOLVE fill:#059669,stroke:#047857,color:#fff
style HUMAN fill:#0ea5e9,stroke:#0369a1,color:#fff
import httpx
from base64 import b64encode
class JiraClient:
def __init__(self, domain: str, email: str, api_token: str):
credentials = b64encode(
f"{email}:{api_token}".encode()
).decode()
self.http = httpx.AsyncClient(
base_url=f"https://{domain}.atlassian.net/rest/api/3",
headers={
"Authorization": f"Basic {credentials}",
"Content-Type": "application/json",
},
timeout=30.0,
)
async def create_issue(self, project_key: str, summary: str,
description: str, issue_type: str = "Task",
priority: str = "Medium",
labels: list[str] = None) -> dict:
payload = {
"fields": {
"project": {"key": project_key},
"summary": summary,
"description": {
"type": "doc",
"version": 1,
"content": [
{
"type": "paragraph",
"content": [
{"type": "text", "text": description}
],
}
],
},
"issuetype": {"name": issue_type},
"priority": {"name": priority},
}
}
if labels:
payload["fields"]["labels"] = labels
response = await self.http.post("/issue", json=payload)
response.raise_for_status()
return response.json()
async def search_issues(self, jql: str, max_results: int = 50) -> list:
response = await self.http.post(
"/search",
json={
"jql": jql,
"maxResults": max_results,
"fields": [
"summary", "status", "assignee",
"priority", "created", "updated",
],
},
)
response.raise_for_status()
return response.json()["issues"]
Let the agent parse unstructured requests — from Slack messages, emails, or voice transcripts — and create well-formatted Jira tickets.
Hear it before you finish reading
Talk to a live CallSphere AI voice agent in your browser — 60 seconds, no signup.
async def create_ticket_from_request(
jira: JiraClient,
agent,
raw_request: str,
project_key: str,
):
# Agent structures the raw input into Jira fields
structured = await agent.run(
prompt=(
f"Parse this request into a Jira ticket.\n"
f"Determine: summary (one line), description (detailed), "
f"issue_type (Bug/Task/Story), priority (Highest/High/Medium/Low/Lowest), "
f"and relevant labels.\n\n"
f"Request: {raw_request}"
)
)
ticket = await jira.create_issue(
project_key=project_key,
summary=structured.summary,
description=structured.description,
issue_type=structured.issue_type,
priority=structured.priority,
labels=structured.labels,
)
return ticket["key"]
JQL (Jira Query Language) gives your agent powerful search capabilities. Use it to gather context before making decisions.
async def get_sprint_health(jira: JiraClient, project_key: str) -> dict:
# Find current sprint issues
in_progress = await jira.search_issues(
f'project = {project_key} AND sprint in openSprints() '
f'AND status = "In Progress"'
)
done = await jira.search_issues(
f'project = {project_key} AND sprint in openSprints() '
f'AND status = "Done"'
)
todo = await jira.search_issues(
f'project = {project_key} AND sprint in openSprints() '
f'AND status = "To Do"'
)
blocked = await jira.search_issues(
f'project = {project_key} AND sprint in openSprints() '
f'AND status = "Blocked"'
)
return {
"total": len(in_progress) + len(done) + len(todo) + len(blocked),
"done": len(done),
"in_progress": len(in_progress),
"todo": len(todo),
"blocked": len(blocked),
"completion_pct": round(
len(done) / max(len(in_progress) + len(done) + len(todo) + len(blocked), 1) * 100
),
}
Moving tickets through workflow states requires knowing the available transitions for the current status.
async def transition_issue(
jira: JiraClient, issue_key: str, target_status: str
):
# Get available transitions
response = await jira.http.get(
f"/issue/{issue_key}/transitions"
)
transitions = response.json()["transitions"]
# Find the transition that leads to our target status
transition = next(
(t for t in transitions if t["to"]["name"] == target_status),
None,
)
if not transition:
available = [t["to"]["name"] for t in transitions]
raise ValueError(
f"Cannot transition to '{target_status}'. "
f"Available: {available}"
)
await jira.http.post(
f"/issue/{issue_key}/transitions",
json={"transition": {"id": transition["id"]}},
)
# Agent-driven bulk status update
async def close_stale_tickets(jira: JiraClient, project_key: str, agent):
stale = await jira.search_issues(
f'project = {project_key} AND status = "In Progress" '
f'AND updated <= -14d'
)
for issue in stale:
key = issue["key"]
summary = issue["fields"]["summary"]
decision = await agent.run(
prompt=f"Ticket {key} ('{summary}') has not been updated in "
f"14 days. Should we move it to Blocked, close it, "
f"or leave it? Explain briefly."
)
if decision.action != "leave":
await transition_issue(jira, key, decision.target_status)
await jira.http.post(
f"/issue/{key}/comment",
json={"body": {
"type": "doc", "version": 1,
"content": [{"type": "paragraph", "content": [
{"type": "text", "text": f"AI Agent: {decision.reason}"}
]}]
}},
)
Jira Cloud V3 API uses Atlassian Document Format (ADF), a JSON-based rich text format. Simple text wraps in paragraph nodes as shown above. For complex formatting (tables, code blocks, bullet lists), build nested ADF node structures. Consider writing a helper function that converts markdown to ADF to simplify agent output formatting.
Still reading? Stop comparing — try CallSphere live.
CallSphere ships complete AI voice agents per industry — 14 tools for healthcare, 10 agents for real estate, 4 specialists for salons. See how it actually handles a call before you book a demo.
Jira Cloud allows roughly 100 requests per minute for basic plans and higher limits for premium. Implement rate limiting on your client side with a token bucket or semaphore. The API returns Retry-After headers on 429 responses — respect those values before retrying.
Yes. Use the assignee field in the create or update payload with the user's Atlassian account ID. To find account IDs, query /rest/api/3/user/search?query=username. Your agent can learn team members' areas of expertise and intelligently assign based on ticket content and past assignments.
#Jira #ProjectManagement #RESTAPI #AIAgents #SprintManagement #AgenticAI #LearnAI #AIEngineering

Written by
Sagar Shankaran· Founder, CallSphere
LinkedInSagar Shankaran is the founder of CallSphere, where he builds production AI voice and chat agents deployed across healthcare, hospitality, real estate, and home services. He writes about agentic AI, LLM engineering, and shipping voice agents that handle real calls in production.
See how AI voice agents work for your industry. Live demo available -- no signup required.
See how Circini's automated incident management pipeline turns alert emails into triaged Jira tickets using Snowflake Cortex AI, GPT-4.1, Airflow & MS Teams.
A founder's guide to the personal AI assistant market: best AI assistant apps, business-grade options, and how CallSphere's voice agent fits in.
A founder's guide to free AI agents, low-code AI agent builders, and how to know when you should pay for a real platform like CallSphere.
Graphiti is the open-source temporal knowledge graph for AI agents in 2026. Learn how bi-temporal memory beats vector RAG for voice agents and long-running LLMs.
Chatbot app vs ChatGPT in 2026: a founder's clear take on the difference, when to use which, and how a real AI chatbot app development works.
How we built a fault-tolerant HVAC emergency triage and tech-dispatch platform on Kubernetes — three-tier CQRS, 11 micro-agents on the OpenAI Agents SDK + LangGraph, NATS JetStream, DTMF/SMS/WebSocket acceptance, circuit breakers, and an evaluation pipeline that catches regressions before they wake a tech at 3 AM.
© 2026 CallSphere Inc. All rights reserved.
Made within San Francisco