GitHub Issues: Automating Sub-Issues and Issue Types
A guide to automating GitHub sub-issues and issue types with the REST API, GitHub CLI, and GraphQL
Overview
Sub-issues and issue types became generally available in April 2025. They are available without an opt-in, and API calls no longer need feature-preview headers.
Sub-issues let you break work into a hierarchy and track progress from a parent issue. Issue types provide an organization-wide classification shared by every repository in the organization. GitHub now supports both features in the REST API, and GitHub CLI v2.94.0 and later has first-class flags for common workflows.
Check out @mickeygousset’s videos for working with sub-issues and issue types! ✨
Sub-Issues
Current Limits and Behavior
- A parent issue can have up to 100 direct sub-issues.
- A hierarchy can contain up to eight levels of nested sub-issues.
- A sub-issue can itself be a parent issue.
- Existing issues can be added from another repository owned by the same user or organization. The REST
Add sub-issueendpoint requires the parent and child repositories to have the same owner. - Each issue can have only one parent. Pass
replace_parent=trueto the REST endpoint when moving a sub-issue to a new parent. - Sub-issue progress appears on the parent issue and in GitHub Projects. REST issue responses include
sub_issues_summaryandparent_issue_url.
See Adding sub-issues for the current product behavior.
Before adding a sub-issue, choose the relationship that best represents how the issues are connected:
- Parent/sub-issue: Use for a hierarchy in which a larger issue contains smaller pieces of work.
- Blocking/blocked by: Use for a dependency in which one issue must be completed before another can proceed. Issue dependencies are generally available.
- Relates to: Use for connected issues when neither hierarchy nor dependency applies. See the
Relates torelationship announcement for more information.
REST API Examples
The examples use gh api, so authentication and the recommended REST headers are handled by GitHub CLI.
Get the Parent Issue
The dedicated REST endpoint replaces the original GraphQL lookup:
1
2
gh api repos/OWNER/REPO/issues/SUB_ISSUE_NUMBER/parent \
--jq '{number, title, url: .html_url, issueType: .type.name}'
List Sub-Issues
Use --paginate because the endpoint returns 30 items per page by default:
1
2
gh api --paginate repos/OWNER/REPO/issues/PARENT_ISSUE_NUMBER/sub_issues \
--jq '.[] | {number, title, url: .html_url, issueType: .type.name}'
Get the Sub-Issue Progress Summary
The standard REST issue response contains the summary:
1
2
gh api repos/OWNER/REPO/issues/PARENT_ISSUE_NUMBER \
--jq '.sub_issues_summary'
An example response looks like:
1
2
3
4
5
{
"total": 3,
"completed": 1,
"percent_completed": 33
}
Add an Existing Issue as a Sub-Issue
The API requires the database ID returned in the REST issue’s id field, not the issue number:
1
2
3
4
5
sub_issue_id=$(gh api repos/OWNER/REPO/issues/SUB_ISSUE_NUMBER --jq '.id')
gh api --method POST \
repos/OWNER/REPO/issues/PARENT_ISSUE_NUMBER/sub_issues \
-F sub_issue_id="$sub_issue_id"
To move an issue that already has a parent, include -F replace_parent=true.
Remove a Sub-Issue
Removing a sub-issue also uses its REST database ID:
1
2
3
4
5
sub_issue_id=$(gh api repos/OWNER/REPO/issues/SUB_ISSUE_NUMBER --jq '.id')
gh api --method DELETE \
repos/OWNER/REPO/issues/PARENT_ISSUE_NUMBER/sub_issue \
-F sub_issue_id="$sub_issue_id"
Reprioritize a Sub-Issue
Place one sub-issue after another in the parent’s ordered list:
1
2
3
4
5
6
7
sub_issue_id=$(gh api repos/OWNER/REPO/issues/SUB_ISSUE_NUMBER --jq '.id')
after_id=$(gh api repos/OWNER/REPO/issues/AFTER_ISSUE_NUMBER --jq '.id')
gh api --method PATCH \
repos/OWNER/REPO/issues/PARENT_ISSUE_NUMBER/sub_issues/priority \
-F sub_issue_id="$sub_issue_id" \
-F after_id="$after_id"
Use before_id instead of after_id to position it before another sub-issue. The complete endpoint reference is in REST API endpoints for sub-issues.
Issue Types
Current Limits and Behavior
- An organization can have up to 25 issue types.
- GitHub provides
Task,Bug, andFeatureby default. Organization owners can edit, disable, or delete them and create custom types. - An issue can have one issue type, and the type is shared across repositories in the organization.
- Disabling a type prevents it from being selected but preserves it on existing issues. Deleting a type permanently removes it.
- Issue types can be used in issue and project filters, such as
type:Bugandno:type.
See Managing issue types in an organization for the current product behavior.
REST API Examples
Read an Issue’s Type
The standard issue response now includes the type object:
1
2
gh api repos/OWNER/REPO/issues/ISSUE_NUMBER \
--jq '{number, title, url: .html_url, issueType: .type.name}'
List Organization Issue Types
1
2
gh api orgs/ORG/issue-types \
--jq '.[] | {id, name, description, color, is_enabled}'
Set or Remove an Issue Type
REST accepts the issue type name when creating or updating an issue. Set the name to null to remove it:
1
2
3
gh api --method PATCH repos/ORG/REPO/issues/ISSUE_NUMBER -f type='Bug'
gh api --method PATCH repos/ORG/REPO/issues/ISSUE_NUMBER -F type=null
Create an Organization Issue Type
Organization owners can create and manage issue types:
1
2
3
4
5
gh api --method POST orgs/ORG/issue-types \
-f name='Epic' \
-f description='A large body of work spanning multiple issues' \
-f color='green' \
-F is_enabled=true
The organization endpoints also support updating and deleting types. See REST API endpoints for issue types.
GitHub CLI Commands
With GitHub CLI v2.94.0 or later, common operations no longer need gh api (GitHub Enterprise Server requires GHES 3.17 or later):
1
2
3
4
5
gh issue create --repo OWNER/REPO --title 'Fix login page' --type 'Bug' --parent 5
gh issue edit 5 --repo OWNER/REPO --add-sub-issue 6
gh issue edit 5 --repo OWNER/REPO --remove-sub-issue 6
gh issue edit 6 --repo OWNER/REPO --type 'Task'
gh issue view 5 --repo OWNER/REPO --json type,parent,subIssues
Existing Helper Scripts
The original scripts remain linked below, but most are now legacy GraphQL implementations superseded by REST endpoints or first-class gh issue options:
| Script | Status |
|---|---|
get-parent-issue-of-issue.sh | Legacy GraphQL; use GET .../parent |
get-sub-issues-of-issue.sh | Legacy GraphQL; use GET .../sub_issues |
get-sub-issues-summary-of-issue.sh | Legacy GraphQL; use GET .../issues/{number} and .sub_issues_summary |
add-sub-issue-to-issue.sh | Legacy GraphQL; use POST .../sub_issues or gh issue edit --add-sub-issue |
remove-sub-issue-from-issue.sh | Legacy GraphQL; use DELETE .../sub_issue or gh issue edit --remove-sub-issue |
get-issue-type-of-issue.sh | Legacy GraphQL; use the REST issue’s .type field |
update-issue-issue-type.sh | Legacy GraphQL; use PATCH .../issues/{number} or gh issue edit --type |
remove-issue-issue-type.sh | Legacy GraphQL; use PATCH .../issues/{number} with type: null |
I verified that all linked files still exist and pass Bash syntax and usage checks. They currently use obsolete GraphQL feature headers, so new automation should prefer the REST and GitHub CLI examples above.
When GraphQL Still Provides Value
REST is now the simplest choice for individual operations. GraphQL remains useful when one request needs a custom-shaped issue hierarchy or related project data that would otherwise require several REST calls. For example, this query retrieves the parent, progress summary, and first page of sub-issues together:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
query ($owner: String!, $repo: String!, $number: Int!) {
repository(owner: $owner, name: $repo) {
issue(number: $number) {
parent {
number
title
}
subIssuesSummary {
total
completed
percentCompleted
}
subIssues(first: 100) {
nodes {
number
title
issueType {
name
}
}
}
}
}
}
This read-only query does not need the old GraphQL-Features: sub_issues or GraphQL-Features: issue_types feature headers.
Summary
Sub-issues and issue types are generally available, supported by dedicated REST endpoints, and built into current versions of GitHub CLI. Prefer gh issue for interactive command-line workflows and gh api with REST for automation. Keep GraphQL for queries that benefit from selecting several related fields in one response.
