Issues
Reference for the issue-tracker endpoints: issues and their lifecycle, comments, description-edit history, parent/child hierarchy, labels, milestones, and the issue-type catalog and per-repository scheme.
Resource overview
Most issue endpoints are scoped to a repository and live under:
/api/v1/repos/{owner}/{repo}/...
This page covers the following sub-resources:
| Sub-resource | Base path |
|---|---|
| Issues | /api/v1/repos/{owner}/{repo}/issues |
| Issue search | /api/v1/repos/{owner}/{repo}/issues/search |
| Comments | /api/v1/repos/{owner}/{repo}/issues/{number}/comments |
| Description history | /api/v1/repos/{owner}/{repo}/issues/{number}/description-history |
| Parent / children | /api/v1/repos/{owner}/{repo}/issues/{number}/parent, /children |
| Labels | /api/v1/repos/{owner}/{repo}/labels |
| Milestones | /api/v1/repos/{owner}/{repo}/milestones |
| Issue-type scheme | /api/v1/repos/{owner}/{repo}/issue-type-scheme |
The issue-type catalog is instance-wide, not per repository, so it sits at the top level:
| Sub-resource | Base path |
|---|---|
| Issue types (read) | /api/v1/issue-types |
| Issue types (administer) | /api/v1/admin/issue-types |
The {number} segment accepts either the bare issue number (70) or the
project-key form (ABC-70) when the repository has an issue-key prefix
configured; a mismatched prefix returns 404.
Issue responses also carry component, version, and workflow-state fields. Those collections are read-only on this page; they are managed through their own endpoints.
Auth & scopes
See conventions.md for the accepted credential types and how scopes are enforced. The operations on this page require:
| Operation | OAuth2 scope | PAT scope |
|---|---|---|
| Read issues, comments, history, children, labels, the issue-type scheme | read:issue |
issue:read |
| Create or update issues and comments; manage labels | write:issue |
issue:write |
| Read milestones | read:repo |
repo:read |
| Create, update, or delete milestones | write:repo |
repo:write |
| Delete an issue or a description-history entry | admin:repo |
repo:admin |
| Manage a repository's issue-type scheme | admin:repo |
repo:admin |
| Create, update, or delete a global issue type | — | admin:system |
Reading the global issue-type catalog (GET /api/v1/issue-types) requires only
an authenticated caller; no resource scope applies.
Read vs. write authorization. Reads enforce the repository's visibility
tier: a private repository is readable only by its members, an internal
repository by any authenticated caller, and a public repository by anyone.
Writes additionally require the issue author or the relevant write/admin
permission — for example, the issue author or a caller with issue-write
permission may update, close, or comment on an issue, while only the repository
owner or an instance admin may delete one. A caller who cannot read a private
repository receives 404, not 403; see
errors.md.
The issue API takes a flat issue_type_id on write but returns a nested
issue_type object on read; see
issues-type-write-read-asymmetry.md.
The issue object
Issue reads (GET, list items, search hits, and the 201/200 bodies of
create and update) return the same object:
{
"id": "<uuid>",
"repo_id": "<uuid>",
"number": 70,
"author_id": "<uuid>",
"author": { "id": "<uuid>", "username": "alice", "...": "..." },
"title": "Login button is misaligned",
"description": "Markdown body of the issue.",
"state": "open",
"locked": false,
"closed_at": null,
"created_at": "2026-01-02T15:04:05Z",
"updated_at": "2026-01-02T15:04:05Z",
"issue_type": {
"id": "00000000-0000-0000-0000-000000000005",
"name": "bug",
"display_name": "Bug",
"description": "A defect or unexpected behaviour",
"color": "ef4444",
"icon": "bug",
"is_subtask": false,
"position": 6
},
"priority": "medium",
"parent_id": null,
"parent": null,
"children_count": 0,
"comments_count": 2,
"due_date": null,
"components": [],
"fix_versions": [],
"affects_versions": [],
"story_points": null,
"input_tokens": null,
"output_tokens": null,
"assumptions": null,
"acceptance_criteria": null,
"links_count": 0,
"blocked_by": [],
"watching": false,
"watchers_count": 1,
"issue_status": "queued",
"labels": [],
"assignees": [],
"milestone": null
}
| Field | Type | Notes |
|---|---|---|
number |
integer | Per-repository issue number; stable identifier in paths. |
state |
string | open or closed. |
issue_status |
string | Workflow status, independent of state: queued, in_progress, blocked, completed, canceled. |
priority |
string | none, low, medium, high, or critical. |
issue_type |
object | Nested type descriptor; null when no type is assigned. Set on write with the flat issue_type_id. |
parent |
object | { id, number, title }, or null when the issue has no parent. Always present. |
assumptions |
string | Markdown; null when unset. |
acceptance_criteria |
string | Markdown; null when unset. |
input_tokens / output_tokens |
integer | Non-negative estimates; rendered as explicit null when unset. |
labels / assignees |
array | Empty arrays when none are attached. |
milestone |
object | { id, title, due_date, state }, or null. |
links_count, blocked_by, watching, and watchers_count are populated on a
single-issue GET and are zero/empty on list and search responses.
Endpoints
| Method | Path | Summary |
|---|---|---|
GET |
/api/v1/repos/{owner}/{repo}/issues |
List issues (paginated, filterable) |
GET |
/api/v1/repos/{owner}/{repo}/issues/{number} |
Fetch a single issue |
POST |
/api/v1/repos/{owner}/{repo}/issues |
Create an issue |
PATCH |
/api/v1/repos/{owner}/{repo}/issues/{number} |
Update an issue |
POST |
/api/v1/repos/{owner}/{repo}/issues/{number}/close |
Close an issue |
POST |
/api/v1/repos/{owner}/{repo}/issues/{number}/reopen |
Reopen an issue |
DELETE |
/api/v1/repos/{owner}/{repo}/issues/{number} |
Delete an issue |
GET |
/api/v1/repos/{owner}/{repo}/issues/search |
Ranked full-text issue search |
GET |
/api/v1/repos/{owner}/{repo}/issues/{number}/comments |
List comments |
POST |
/api/v1/repos/{owner}/{repo}/issues/{number}/comments |
Add a comment |
PATCH |
/api/v1/repos/{owner}/{repo}/issues/{number}/comments/{cid} |
Edit a comment |
DELETE |
/api/v1/repos/{owner}/{repo}/issues/{number}/comments/{cid} |
Delete a comment |
GET |
/api/v1/repos/{owner}/{repo}/issues/{number}/description-history |
List description edits |
DELETE |
/api/v1/repos/{owner}/{repo}/issues/{number}/description-history/{id} |
Delete a history entry |
GET |
/api/v1/repos/{owner}/{repo}/issues/{number}/children |
List child issues |
PUT |
/api/v1/repos/{owner}/{repo}/issues/{number}/parent |
Set or change the parent |
DELETE |
/api/v1/repos/{owner}/{repo}/issues/{number}/parent |
Clear the parent |
GET |
/api/v1/repos/{owner}/{repo}/labels |
List labels |
POST |
/api/v1/repos/{owner}/{repo}/labels |
Create a label |
GET |
/api/v1/repos/{owner}/{repo}/labels/{id} |
Fetch a label |
PATCH |
/api/v1/repos/{owner}/{repo}/labels/{id} |
Update a label |
DELETE |
/api/v1/repos/{owner}/{repo}/labels/{id} |
Delete a label |
GET |
/api/v1/repos/{owner}/{repo}/milestones |
List milestones |
POST |
/api/v1/repos/{owner}/{repo}/milestones |
Create a milestone |
GET |
/api/v1/repos/{owner}/{repo}/milestones/{id} |
Fetch a milestone |
PATCH |
/api/v1/repos/{owner}/{repo}/milestones/{id} |
Update a milestone |
DELETE |
/api/v1/repos/{owner}/{repo}/milestones/{id} |
Delete a milestone |
GET |
/api/v1/issue-types |
List global issue types |
GET |
/api/v1/issue-types/{id} |
Fetch a global issue type |
POST |
/api/v1/admin/issue-types |
Create an issue type |
PATCH |
/api/v1/admin/issue-types/{id} |
Update an issue type |
DELETE |
/api/v1/admin/issue-types/{id} |
Delete an issue type |
GET |
/api/v1/repos/{owner}/{repo}/issue-type-scheme |
Read the repository's scheme |
PUT |
/api/v1/repos/{owner}/{repo}/issue-type-scheme |
Replace the repository's scheme |
GET /api/v1/repos/{owner}/{repo}/issues
List a repository's issues. Returns the page envelope described in Pagination.
Query parameters
| Name | Type | Default | Description |
|---|---|---|---|
page |
integer | 1 |
See Pagination. |
per_page |
integer | 25 |
See Pagination. |
state |
string | all | One of open, closed, all. An unrecognized value returns 400. |
status |
string | — | Filter by workflow status (queued, in_progress, blocked, completed, canceled). An unrecognized value returns 400. |
type |
string | — | Filter by issue-type name (for example bug). |
priority |
string | — | Filter by priority. |
label |
string | — | Filter by label name. |
milestone_id |
uuid | — | Filter by milestone. A malformed UUID returns 400. |
has_parent |
boolean | — | true returns only issues that have a parent. |
component / fix_version / affects_version |
string | — | Filter by associated component or version name. |
q |
string | — | Case-insensitive title and full-text match, combined with the other filters. |
A due_before / due_after pair (each YYYY-MM-DD), overdue,
story_points_min / story_points_max, unestimated, and the token-budget
filters (tokens_min, tokens_max, untokenized, output_tokens_min,
output_tokens_max, no_output_tokens) are also accepted.
Response
{
"items": [ { "...": "the issue object" } ],
"total": 137,
"page": 1,
"per_page": 25
}
Status codes
| Status | When |
|---|---|
200 OK |
Issues returned. |
400 Bad Request |
Invalid state, status, or milestone_id. |
404 Not Found |
Repository missing, or private and not readable — see Errors. |
Example
curl -H "Authorization: Bearer <token>" \
"https://api.gitvetrix.com/api/v1/repos/{owner}/{repo}/issues?state=open&label=bug&per_page=50"
GET /api/v1/repos/{owner}/{repo}/issues/{number}
Fetch one issue, fully hydrated.
Path parameters
| Name | Type | Description |
|---|---|---|
number |
string | Issue number (70) or project key (ABC-70). |
Response — the issue object.
Status codes
| Status | When |
|---|---|
200 OK |
Issue returned. |
404 Not Found |
Issue or repository missing, or private and not readable. |
Example
curl -H "Authorization: Bearer <token>" \
https://api.gitvetrix.com/api/v1/repos/{owner}/{repo}/issues/70
POST /api/v1/repos/{owner}/{repo}/issues
Create an issue. The request is strictly decoded: an unknown field — including
the read-shaped issue_type object — is rejected with 400.
Request body
{
"title": "Login button is misaligned",
"description": "Markdown body.",
"issue_type_id": "00000000-0000-0000-0000-000000000005",
"priority": "medium",
"milestone_id": "<uuid>",
"label_ids": ["<uuid>"],
"assignee_ids": ["<uuid>"],
"story_points": 3,
"acceptance_criteria": "Given/when/then.",
"assumptions": "None."
}
| Field | Required | Notes |
|---|---|---|
title |
yes | Non-empty. |
issue_type_id |
yes | UUID of an existing issue type. Set the type with this flat scalar, never the nested issue_type. |
description |
no | Markdown body. |
priority |
no | One of the priority values; defaults to none. |
milestone_id |
no | UUID. |
label_ids |
no | UUIDs of existing labels. |
inline_labels |
no | [{ "name", "color" }] to create-or-reuse labels by name; requires label-create permission. |
assignee_ids |
no | UUIDs. |
parent_id |
no | UUID of an existing issue in the same repository to set as the parent at create time. A malformed, missing, or cross-repository value returns 400. |
due_date |
no | YYYY-MM-DD. |
story_points |
no | Non-negative integer. |
input_tokens / output_tokens |
no | Non-negative integers. |
issue_status |
no | Workflow status; defaults to queued. |
assumptions / acceptance_criteria |
no | Markdown; empty string stores no value. |
component_ids / fix_version_ids / affects_version_ids |
no | UUIDs of existing associations. |
Set the parent at create time with parent_id (the parent issue's UUID). To
re-parent an existing issue afterward, use PUT .../issues/{number}/parent with
the integer parent_number instead; parent_id is rejected on PATCH
(update).
Response — 201 Created with the issue object.
Status codes
| Status | When |
|---|---|
201 Created |
Issue created. |
400 Bad Request |
Missing title or issue_type_id, an invalid value, or an unknown/read-shaped field. |
403 Forbidden |
inline_labels supplied without label-create permission. |
404 Not Found |
Repository missing or not readable. |
Example
curl -X POST -H "Authorization: Bearer <token>" \
-H "Content-Type: application/json" \
-d '{"title":"Login button is misaligned","issue_type_id":"00000000-0000-0000-0000-000000000005"}' \
https://api.gitvetrix.com/api/v1/repos/{owner}/{repo}/issues
PATCH /api/v1/repos/{owner}/{repo}/issues/{number}
Update an issue. Use PATCH for every field change, including state-adjacent
fields such as issue_status; PUT is not defined for this path and returns
405 Method Not Allowed. Only the fields present in the body change. The body
is strictly decoded — parent_id and the read-shaped issue_type are rejected
with 400.
Path parameters
| Name | Type | Description |
|---|---|---|
number |
string | Issue number or project key. |
Request body — any subset of:
{
"title": "New title",
"description": "New body.",
"issue_type_id": "<uuid>",
"priority": "high",
"issue_status": "in_progress",
"milestone_id": null,
"locked": true,
"label_ids": ["<uuid>"],
"assignee_ids": ["<uuid>"],
"story_points": null,
"acceptance_criteria": "...",
"assumptions": "..."
}
For the nullable fields, the three states are distinct: omit the key to leave it
unchanged, send null to clear it, and send a value to set it. This applies to
milestone_id, story_points, input_tokens, and output_tokens. A present
label_ids or assignee_ids array (including an empty array) replaces the
current set.
Response — 200 OK with the issue object.
Status codes
| Status | When |
|---|---|
200 OK |
Issue updated. |
400 Bad Request |
Empty title, an invalid value, or an unknown/disallowed field. |
403 Forbidden |
Caller is neither the author nor holds issue-write permission. |
404 Not Found |
Issue or repository missing or not readable. |
405 Method Not Allowed |
PUT was used instead of PATCH. |
Example
curl -X PATCH -H "Authorization: Bearer <token>" \
-H "Content-Type: application/json" \
-d '{"issue_status":"in_progress","priority":"high"}' \
https://api.gitvetrix.com/api/v1/repos/{owner}/{repo}/issues/70
POST /api/v1/repos/{owner}/{repo}/issues/{number}/close
Close an issue. The author or a caller with issue-write permission may close it.
Status codes
| Status | When |
|---|---|
204 No Content |
Issue closed. |
403 Forbidden |
Caller may not modify the issue. |
404 Not Found |
Issue or repository missing or not readable. |
Example
curl -X POST -H "Authorization: Bearer <token>" \
https://api.gitvetrix.com/api/v1/repos/{owner}/{repo}/issues/70/close
POST /api/v1/repos/{owner}/{repo}/issues/{number}/reopen
Reopen a closed issue. Same authorization and status codes as close, returning
204 No Content on success.
DELETE /api/v1/repos/{owner}/{repo}/issues/{number}
Permanently delete an issue and its dependent rows (comments, labels, assignees, links, history, watchers, and associations). Only the repository owner or an instance admin may delete an issue.
Status codes
| Status | When |
|---|---|
204 No Content |
Issue deleted. |
403 Forbidden |
Caller lacks delete permission. |
404 Not Found |
Issue or repository missing or not readable. |
Example
curl -X DELETE -H "Authorization: Bearer <token>" \
https://api.gitvetrix.com/api/v1/repos/{owner}/{repo}/issues/70
GET /api/v1/repos/{owner}/{repo}/issues/search
Ranked full-text issue search. Returns a bare JSON array of issue objects, each
with an added rank, ordered by relevance. This complements the list endpoint's
q filter, which returns the standard page envelope in list order.
Query parameters
| Name | Type | Default | Description |
|---|---|---|---|
q |
string | — | Search query. Required. |
limit |
integer | 25 |
Maximum hits to return. |
facets |
boolean | false |
When true, returns aggregations alongside hits where the search backend supports them. |
Response
[
{ "...": "the issue object", "rank": 0.41 }
]
Status codes
| Status | When |
|---|---|
200 OK |
Hits returned (possibly empty). |
400 Bad Request |
q is missing. |
404 Not Found |
Repository missing or not readable. |
Example
curl -H "Authorization: Bearer <token>" \
"https://api.gitvetrix.com/api/v1/repos/{owner}/{repo}/issues/search?q=login&limit=10"
GET /api/v1/repos/{owner}/{repo}/issues/{number}/comments
List an issue's comments in creation order. Returns a bare JSON array.
Response
[
{
"id": "<uuid>",
"issue_id": "<uuid>",
"author_id": "<uuid>",
"author": { "id": "<uuid>", "username": "alice", "...": "..." },
"body": "Comment text.",
"created_at": "2026-01-02T15:04:05Z",
"updated_at": "2026-01-02T15:04:05Z"
}
]
Status codes
| Status | When |
|---|---|
200 OK |
Comments returned. |
404 Not Found |
Issue or repository missing or not readable. |
POST /api/v1/repos/{owner}/{repo}/issues/{number}/comments
Add a comment.
Request body
{ "body": "Comment text." }
body is required.
Response — 201 Created with the comment object shown above.
Status codes
| Status | When |
|---|---|
201 Created |
Comment added. |
400 Bad Request |
Missing or empty body. |
404 Not Found |
Issue or repository missing or not readable. |
Example
curl -X POST -H "Authorization: Bearer <token>" \
-H "Content-Type: application/json" \
-d '{"body":"Reproduced on the latest build."}' \
https://api.gitvetrix.com/api/v1/repos/{owner}/{repo}/issues/70/comments
PATCH /api/v1/repos/{owner}/{repo}/issues/{number}/comments/{cid}
Edit a comment. Only the comment author may edit it.
Path parameters
| Name | Type | Description |
|---|---|---|
cid |
uuid | Comment identifier. |
Request body
{ "body": "Edited text." }
Status codes
| Status | When |
|---|---|
200 OK |
Comment updated. |
400 Bad Request |
Invalid comment id or empty body. |
403 Forbidden |
Caller is not the comment author. |
404 Not Found |
Comment or repository missing. |
DELETE /api/v1/repos/{owner}/{repo}/issues/{number}/comments/{cid}
Delete a comment. The comment author or an instance admin may delete it.
Status codes
| Status | When |
|---|---|
204 No Content |
Comment deleted. |
400 Bad Request |
Invalid comment id. |
403 Forbidden |
Caller is neither the author nor an admin. |
404 Not Found |
Comment or repository missing. |
GET /api/v1/repos/{owner}/{repo}/issues/{number}/description-history
List the recorded edits to an issue's description, newest first. Returns a bare JSON array.
Response
[
{
"id": "<uuid>",
"issue_id": "<uuid>",
"body_before": "Old body.",
"body_after": "New body.",
"edited_by": "<uuid>",
"edited_at": "2026-01-02T15:04:05Z",
"editor_username": "alice",
"editor_display_name": "Alice"
}
]
Status codes
| Status | When |
|---|---|
200 OK |
History returned. |
404 Not Found |
Issue or repository missing or not readable. |
DELETE /api/v1/repos/{owner}/{repo}/issues/{number}/description-history/{id}
Delete a single description-history entry. Restricted to repository admins and instance admins; every deletion is recorded in the audit log.
Path parameters
| Name | Type | Description |
|---|---|---|
id |
uuid | History entry identifier. |
Status codes
| Status | When |
|---|---|
204 No Content |
Entry deleted. |
400 Bad Request |
Invalid entry id. |
403 Forbidden |
Caller lacks history-delete permission. |
404 Not Found |
Entry or repository missing. |
GET /api/v1/repos/{owner}/{repo}/issues/{number}/children
List the direct child issues of an issue. Returns a bare JSON array of issue objects.
Status codes
| Status | When |
|---|---|
200 OK |
Children returned (possibly empty). |
404 Not Found |
Issue or repository missing or not readable. |
PUT /api/v1/repos/{owner}/{repo}/issues/{number}/parent
Re-parent an existing issue. (At create time, set the parent inline with the
parent_id UUID on POST .../issues instead.) The author, the repository
owner, or an instance admin may set it.
Request body
{ "parent_number": 12 }
parent_number is the issue number of the parent within the same repository. An
issue cannot be its own parent. The parent must not be a sub-task type, and a
support issue cannot be the parent of an epic or story.
Status codes
| Status | When |
|---|---|
204 No Content |
Parent set. |
400 Bad Request |
Missing/non-positive parent_number, or it equals the issue itself. |
403 Forbidden |
Caller may not modify the issue. |
404 Not Found |
Issue, parent, or repository missing or not readable. |
422 Unprocessable Entity |
Parent is a sub-task, or a support issue would parent an epic or story. |
Example
curl -X PUT -H "Authorization: Bearer <token>" \
-H "Content-Type: application/json" \
-d '{"parent_number":12}' \
https://api.gitvetrix.com/api/v1/repos/{owner}/{repo}/issues/70/parent
DELETE /api/v1/repos/{owner}/{repo}/issues/{number}/parent
Clear an issue's parent link. Same authorization as setting the parent.
Status codes
| Status | When |
|---|---|
204 No Content |
Parent cleared. |
403 Forbidden |
Caller may not modify the issue. |
404 Not Found |
Issue or repository missing or not readable. |
GET /api/v1/repos/{owner}/{repo}/labels
List a repository's labels. Returns a bare JSON array.
Response
[
{
"id": "<uuid>",
"repo_id": "<uuid>",
"name": "bug",
"color": "e11d48",
"description": null
}
]
color is a 6-character hex value without a leading #.
Status codes
| Status | When |
|---|---|
200 OK |
Labels returned. |
404 Not Found |
Repository missing or not readable. |
POST /api/v1/repos/{owner}/{repo}/labels
Create a label.
Request body
{ "name": "bug", "color": "e11d48", "description": "Something is broken" }
| Field | Required | Notes |
|---|---|---|
name |
yes | 1–64 characters; unique within the repository. |
color |
yes | 6-character hex without #. |
description |
no | Up to 255 characters. |
Status codes
| Status | When |
|---|---|
201 Created |
Label created. |
400 Bad Request |
Missing name, over-length name/description, or malformed color. |
403 Forbidden |
Caller lacks label-create permission. |
404 Not Found |
Repository missing. |
409 Conflict |
A label with that name already exists. |
GET /api/v1/repos/{owner}/{repo}/labels/{id}
Fetch one label by UUID. Returns the label object shown above.
Status codes
| Status | When |
|---|---|
200 OK |
Label returned. |
404 Not Found |
Label or repository missing or not readable. |
PATCH /api/v1/repos/{owner}/{repo}/labels/{id}
Update a label. Only the fields present change.
Request body — any subset of name, color, description. The same
validation as create applies.
Status codes
| Status | When |
|---|---|
200 OK |
Label updated. |
400 Bad Request |
Empty/over-length name, over-length description, or malformed color. |
403 Forbidden |
Caller lacks label-update permission. |
404 Not Found |
Label or repository missing. |
409 Conflict |
The new name collides with an existing label. |
DELETE /api/v1/repos/{owner}/{repo}/labels/{id}
Delete a label. The path segment is normally a label UUID. A non-UUID segment is
treated as a label name (deprecated); a name-based deletion adds a
Deprecation: true header and a Link header pointing at the UUID-based route.
Status codes
| Status | When |
|---|---|
204 No Content |
Label deleted. |
403 Forbidden |
Caller lacks label-delete permission. |
404 Not Found |
Label or repository missing. |
GET /api/v1/repos/{owner}/{repo}/milestones
List milestones with their open/closed issue counts. Returns a bare JSON array.
Query parameters
| Name | Type | Default | Description |
|---|---|---|---|
state |
string | all | Filter by open or closed. |
Response
[
{
"id": "<uuid>",
"repo_id": "<uuid>",
"title": "v1.0",
"description": "First stable release.",
"due_date": "2026-03-01",
"state": "open",
"created_at": "2026-01-02T15:04:05Z",
"closed_at": null,
"open_issues": 4,
"closed_issues": 11
}
]
Status codes
| Status | When |
|---|---|
200 OK |
Milestones returned. |
404 Not Found |
Repository missing or not readable. |
POST /api/v1/repos/{owner}/{repo}/milestones
Create a milestone.
Request body
{ "title": "v1.0", "description": "First stable release.", "due_date": "2026-03-01" }
| Field | Required | Notes |
|---|---|---|
title |
yes | Non-empty. |
description |
no | Plain text. |
due_date |
no | YYYY-MM-DD. |
Status codes
| Status | When |
|---|---|
201 Created |
Milestone created. |
400 Bad Request |
Missing title or malformed due_date. |
403 Forbidden |
Caller lacks repository-write permission. |
404 Not Found |
Repository missing. |
GET /api/v1/repos/{owner}/{repo}/milestones/{id}
Fetch one milestone by UUID, with its issue counts.
Status codes
| Status | When |
|---|---|
200 OK |
Milestone returned. |
400 Bad Request |
Malformed milestone id. |
404 Not Found |
Milestone or repository missing or not readable. |
PATCH /api/v1/repos/{owner}/{repo}/milestones/{id}
Update a milestone. Only the fields present change. Send due_date: "" to clear
the due date. state accepts open or closed.
Status codes
| Status | When |
|---|---|
200 OK |
Milestone updated. |
400 Bad Request |
Malformed id, empty title, bad due_date, or invalid state. |
403 Forbidden |
Caller lacks repository-write permission. |
404 Not Found |
Milestone or repository missing. |
DELETE /api/v1/repos/{owner}/{repo}/milestones/{id}
Delete a milestone.
Status codes
| Status | When |
|---|---|
204 No Content |
Milestone deleted. |
400 Bad Request |
Malformed milestone id. |
403 Forbidden |
Caller lacks repository-write permission. |
404 Not Found |
Repository missing. |
GET /api/v1/issue-types
List the instance-wide issue types, ordered by position. Any authenticated caller may read them. Returns a bare JSON array of issue-type objects:
[
{
"id": "00000000-0000-0000-0000-000000000005",
"name": "bug",
"display_name": "Bug",
"description": "A defect or unexpected behaviour",
"color": "ef4444",
"icon": "bug",
"is_subtask": false,
"position": 6
}
]
The built-in types use stable UUIDs paired with these canonical name values:
...0001 epic, ...0002 story, ...0003 task, ...0004 analysis,
...0005 bug, ...0006 feature, and ...0007 support. The name is
immutable, but an instance's display_name for a type may be customized — for
example, feature ships with the display name Deficiency. Use the id value
as issue_type_id when creating an issue.
Status codes
| Status | When |
|---|---|
200 OK |
Types returned. |
GET /api/v1/issue-types/{id}
Fetch one issue type by UUID.
Status codes
| Status | When |
|---|---|
200 OK |
Type returned. |
400 Bad Request |
Malformed id. |
404 Not Found |
Type missing. |
POST /api/v1/admin/issue-types
Create an issue type. Instance admins only.
Request body
{
"name": "incident",
"display_name": "Incident",
"description": "A production incident",
"color": "ef4444",
"icon_name": "alert",
"is_subtask": false,
"position": 8
}
| Field | Required | Notes |
|---|---|---|
name |
yes | Lower-cased and trimmed; the canonical identifier. |
display_name |
yes | Human-readable label. |
color |
yes | 6-character hex without #. |
description / icon_name / is_subtask / position |
no |
Status codes
| Status | When |
|---|---|
201 Created |
Type created. |
400 Bad Request |
Missing name/display_name or malformed color. |
403 Forbidden |
Caller is not an instance admin. |
PATCH /api/v1/admin/issue-types/{id}
Update an issue type. Instance admins only. The canonical name is immutable;
display_name, description, color, icon_name, and position may change.
Status codes
| Status | When |
|---|---|
200 OK |
Type updated. |
400 Bad Request |
Malformed id, empty display_name, or malformed color. |
403 Forbidden |
Caller is not an instance admin. |
404 Not Found |
Type missing. |
DELETE /api/v1/admin/issue-types/{id}
Delete an issue type. Instance admins only. A type that is still referenced by any issue cannot be deleted.
Status codes
| Status | When |
|---|---|
204 No Content |
Type deleted. |
400 Bad Request |
Malformed id. |
403 Forbidden |
Caller is not an instance admin. |
404 Not Found |
Type missing. |
409 Conflict |
The type is still in use. |
GET /api/v1/repos/{owner}/{repo}/issue-type-scheme
Read the ordered set of issue types enabled for a repository. Returns a bare JSON array of scheme items:
[
{ "issue_type": { "id": "<uuid>", "name": "epic", "...": "..." }, "position": 0 }
]
Status codes
| Status | When |
|---|---|
200 OK |
Scheme returned. |
404 Not Found |
Repository missing or not readable. |
PUT /api/v1/repos/{owner}/{repo}/issue-type-scheme
Replace the repository's active set of issue types. Requires a repository admin, the organization owner, or an instance admin.
Request body
{ "type_ids": ["<uuid>", "<uuid>"] }
type_ids is the ordered list of issue-type UUIDs to enable. An empty array
clears the scheme.
Status codes
| Status | When |
|---|---|
200 OK |
Scheme replaced. |
400 Bad Request |
Malformed body or a non-UUID entry. |
403 Forbidden |
Caller may not manage the scheme. |
404 Not Found |
Repository missing. |
Errors
These endpoints use the shared error envelope and status-code conventions in errors.md. Resource-specific behavior worth noting:
- A private repository the caller cannot read returns
404, not403, on every read here — see errors.md. - Creating or updating an issue strictly decodes the body: an unknown field, a
bare-string or nested
issue_type, orparent_idon update returns400with a hint indetail. See issues-type-write-read-asymmetry.md. - Duplicate label names and in-use issue types return
409 Conflict. - Disallowed parent relationships return
422 Unprocessable Entity.
Rate limits
These endpoints are metered under the standard scopes described in
rate-limits.md: reads under api.read and mutations under
api.write. The ranked search endpoint,
GET /api/v1/repos/{owner}/{repo}/issues/search, is metered under
api.expensive.