By Sagar Shankaran, Founder of CallSphere
Build an AI agent that automates GitHub workflows including issue triage, pull request code reviews, and release note generation using the GitHub API and webhook event processing.
Key takeaways
GitHub is the center of the development workflow. An AI agent integrated with GitHub can triage incoming issues, review pull request diffs, suggest code improvements, auto-label PRs, generate release notes from commit history, and enforce coding standards — reducing toil for engineering teams and accelerating the review cycle.
The combination of GitHub's REST and GraphQL APIs with webhook events gives your agent real-time awareness of repository activity and the ability to take automated actions.
Use a GitHub App or a fine-grained personal access token. GitHub Apps are preferred for production because they have granular permissions and higher rate limits.
Hear it before you finish reading
Talk to a live CallSphere AI voice agent for IT support in your browser — 60 seconds, no signup.
flowchart LR
REL(["Release of<br/>AI Agent for GitHub"])
NEW1["What's new<br/>flagship feature 1"]
NEW2["What's new<br/>flagship feature 2"]
NEW3["What's new<br/>flagship feature 3"]
BREAK{"Breaking<br/>changes?"}
MIG["Migration steps"]
UPG(["Upgrade now"])
WAIT(["Pin current,<br/>upgrade later"])
REL --> NEW1
REL --> NEW2
REL --> NEW3
NEW1 --> BREAK
NEW2 --> BREAK
NEW3 --> BREAK
BREAK -->|Yes| MIG --> UPG
BREAK -->|No| UPG
BREAK -->|Risk averse| WAIT
style REL fill:#4f46e5,stroke:#4338ca,color:#fff
style BREAK fill:#f59e0b,stroke:#d97706,color:#1f2937
style UPG fill:#059669,stroke:#047857,color:#fff
style WAIT fill:#0ea5e9,stroke:#0369a1,color:#fff
import httpx
import hashlib
import hmac
class GitHubClient:
def __init__(self, token: str):
self.http = httpx.AsyncClient(
base_url="https://api.github.com",
headers={
"Authorization": f"Bearer {token}",
"Accept": "application/vnd.github+json",
"X-GitHub-Api-Version": "2022-11-28",
},
timeout=30.0,
)
async def create_issue_comment(
self, owner: str, repo: str, issue_number: int, body: str
):
response = await self.http.post(
f"/repos/{owner}/{repo}/issues/{issue_number}/comments",
json={"body": body},
)
response.raise_for_status()
return response.json()
async def get_pull_request_diff(
self, owner: str, repo: str, pr_number: int
) -> str:
response = await self.http.get(
f"/repos/{owner}/{repo}/pulls/{pr_number}",
headers={"Accept": "application/vnd.github.diff"},
)
response.raise_for_status()
return response.text
async def add_labels(
self, owner: str, repo: str, issue_number: int, labels: list[str]
):
response = await self.http.post(
f"/repos/{owner}/{repo}/issues/{issue_number}/labels",
json={"labels": labels},
)
response.raise_for_status()
Set up a webhook endpoint that receives GitHub events and routes them to the appropriate agent handler.
from fastapi import FastAPI, Request, HTTPException
app = FastAPI()
WEBHOOK_SECRET = "your-webhook-secret"
def verify_github_signature(payload: bytes, signature: str) -> bool:
expected = "sha256=" + hmac.new(
WEBHOOK_SECRET.encode(), payload, hashlib.sha256
).hexdigest()
return hmac.compare_digest(expected, signature)
@app.post("/github/webhook")
async def handle_github_webhook(request: Request):
body = await request.body()
signature = request.headers.get("X-Hub-Signature-256", "")
if not verify_github_signature(body, signature):
raise HTTPException(status_code=401, detail="Invalid signature")
event_type = request.headers.get("X-GitHub-Event")
payload = await request.json()
handlers = {
"issues": handle_issue_event,
"pull_request": handle_pr_event,
"release": handle_release_event,
}
handler = handlers.get(event_type)
if handler:
await handler(payload)
return {"status": "ok"}
When a new issue is opened, the agent analyzes the title and body, assigns labels, estimates complexity, and optionally suggests an assignee.
async def handle_issue_event(payload: dict):
if payload["action"] != "opened":
return
issue = payload["issue"]
owner = payload["repository"]["owner"]["login"]
repo = payload["repository"]["name"]
analysis = await agent.run(
prompt=(
f"Analyze this GitHub issue and provide:\n"
f"1. Labels (from: bug, feature, docs, question, enhancement)\n"
f"2. Priority (P0-P3)\n"
f"3. A brief acknowledgment comment\n\n"
f"Title: {issue['title']}\n"
f"Body: {issue['body'] or 'No description provided'}"
)
)
github = GitHubClient(token=GITHUB_TOKEN)
# Apply labels
await github.add_labels(
owner, repo, issue["number"], analysis.labels
)
# Post triage comment
comment = (
f"Thanks for opening this issue!\n\n"
f"**AI Triage Summary:**\n"
f"- **Priority:** {analysis.priority}\n"
f"- **Category:** {', '.join(analysis.labels)}\n\n"
f"{analysis.comment}"
)
await github.create_issue_comment(
owner, repo, issue["number"], comment
)
The agent reads the PR diff, identifies potential issues, and posts a structured review comment.
async def handle_pr_event(payload: dict):
if payload["action"] != "opened":
return
pr = payload["pull_request"]
owner = payload["repository"]["owner"]["login"]
repo = payload["repository"]["name"]
github = GitHubClient(token=GITHUB_TOKEN)
diff = await github.get_pull_request_diff(owner, repo, pr["number"])
review = await agent.run(
prompt=(
f"Review this pull request diff. Check for:\n"
f"- Bugs or logic errors\n"
f"- Security vulnerabilities\n"
f"- Performance concerns\n"
f"- Missing error handling\n"
f"- Code style issues\n\n"
f"PR Title: {pr['title']}\n"
f"PR Description: {pr['body'] or 'None'}\n\n"
f"Diff:\n{diff[:12000]}" # Truncate large diffs
)
)
# Post as a PR review
await github.http.post(
f"/repos/{owner}/{repo}/pulls/{pr['number']}/reviews",
json={
"body": review.summary,
"event": "COMMENT", # APPROVE, REQUEST_CHANGES, or COMMENT
},
)
Generate structured release notes from commits between two tags.
Still reading? Stop comparing — try CallSphere live.
See the IT support AI agent handle a real call — complete, industry-specific, and live in your browser. No signup.
async def generate_release_notes(
github: GitHubClient,
owner: str,
repo: str,
tag_name: str,
previous_tag: str,
) -> str:
# Get commits between tags
response = await github.http.get(
f"/repos/{owner}/{repo}/compare/{previous_tag}...{tag_name}"
)
comparison = response.json()
commits = [
f"- {c['commit']['message'].split(chr(10))[0]}"
for c in comparison["commits"]
]
commit_log = "\n".join(commits)
notes = await agent.run(
prompt=(
f"Generate release notes from these commits. Group by:\n"
f"- Features, Bug Fixes, Improvements, Breaking Changes\n"
f"Use markdown formatting.\n\n"
f"Commits:\n{commit_log}"
)
)
return notes.content
Split the diff by file and process each file separately, then aggregate the results. Prioritize reviewing files that changed the most lines or that are in critical paths (authentication, payment, database migration files). You can also use the GitHub API to fetch individual file patches instead of the entire diff.
At minimum: issues:write for labeling and commenting, pull_requests:write for posting reviews, contents:read for accessing diffs and commits, and metadata:read. For release note automation, add contents:write to create releases.
Check the sender field in the webhook payload. If payload["sender"]["login"] matches your GitHub App's bot username (typically your-app-name[bot]), skip processing. Also set "active": true with specific event filters on the webhook to reduce unnecessary deliveries.
#GitHub #GitHubAPI #CodeReview #DevOps #AIAgents #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
Watch how CallSphere handles real customer calls, schedules appointments, and processes payments — live.