Adds a comprehensive opencode skill under .opencode/skills/forgejo-api/
covering all 473 Forgejo REST API endpoints across 25 reference categories.
- 78 files, 23,000+ lines, 149 distinct path parameter types
- Every curl command parameterised ({owner}/{repo}/{index}/etc) and
tested against the live git.cleverthis.com server
- SKILL.md: 917-line entry point with quick-answer curl commands (35),
jq cheat sheet for chaining API calls, 14 decision trees, 12 critical
concepts (exclusive labels, lazy mergeability, SHA locking, auto-close
keywords, search envelope differences, 412 stale-edit protection), full
HTTP status code table, and environment variable reference
- references/pull-requests/: CRUD, 6 merge styles, automerge, server-side
rebase without local clone, inline review comments, diff/patch
- references/issues/: comments, reactions, attachments, dependencies,
time tracking, stopwatches, pinning
- references/labels/: repo + org labels, exclusive label groups,
GET/POST/PUT/DELETE on issues and PRs
- references/ci-actions/ + references/commit-statuses/: workflow runs,
dispatch, secrets, variables, quality gate verification
- references/web-interface/ci-logs.md: step-by-step CI log access via
CSRF web session (not available through REST API)
- references/complex-workflows/: 10 multi-step recipes including
PR review cycle, issue lifecycle, CI status check, server-side rebase,
automerge, release workflow, org setup, fork contribution
Forgejo Issues API
Base URL: https://git.cleverthis.com/api/v1
All endpoints require authentication via Authorization: token ${FORGEJO_PAT} header.
List Repository Issues
GET /repos/{owner}/{repo}/issues
Returns issues (and optionally pull requests) for a repository.
Parameters
| Parameter | Type | In | Description |
|---|---|---|---|
owner |
string | path | Required. Repository owner |
repo |
string | path | Required. Repository name |
state |
string | query | Filter by state: open, closed, all. Default: open |
labels |
string | query | Comma-separated list of label names or IDs |
milestones |
string | query | Comma-separated list of milestone names or IDs |
q |
string | query | Search string (searches title and body) |
type |
string | query | Filter by type: issues, pulls. Default: returns both |
page |
integer | query | Page number (1-based). Default: 1 |
limit |
integer | query | Page size. Default: server default (usually 20, max 50) |
sort |
string | query | Sort field. Options: oldest, recentupdate, leastupdate, mostcomment, leastcomment, priority. Default: newest first |
since |
string | query | Only show issues updated after this date (ISO 8601 format, e.g. 2024-01-01T00:00:00Z) |
before |
string | query | Only show issues updated before this date (ISO 8601 format) |
created_by |
string | query | Filter by creator username |
assigned_by |
string | query | Filter by assignee username |
mentioned_by |
string | query | Filter by mentioned username |
Response
Returns 200 with an array of Issue objects. Pagination info is in response headers:
X-Total-Count— total number of matching issuesLink— standard pagination links (first, prev, next, last)
Examples
# List all open issues
curl -s \
-H "Authorization: token ${FORGEJO_PAT}" \
"${FORGEJO_URL}/api/v1/repos/{owner}/{repo}/issues?state=open&type=issues"
# List closed issues with pagination
curl -s \
-H "Authorization: token ${FORGEJO_PAT}" \
"${FORGEJO_URL}/api/v1/repos/{owner}/{repo}/issues?state=closed&page=2&limit=10"
# Search issues by keyword
curl -s \
-H "Authorization: token ${FORGEJO_PAT}" \
"${FORGEJO_URL}/api/v1/repos/{owner}/{repo}/issues?q=login+bug&state=open"
# Filter by labels (comma-separated label names)
curl -s \
-H "Authorization: token ${FORGEJO_PAT}" \
"${FORGEJO_URL}/api/v1/repos/{owner}/{repo}/issues?labels=bug,critical"
# Filter by milestone
curl -s \
-H "Authorization: token ${FORGEJO_PAT}" \
"${FORGEJO_URL}/api/v1/repos/{owner}/{repo}/issues?milestones={milestone_name}"
# Issues updated since a date
curl -s \
-H "Authorization: token ${FORGEJO_PAT}" \
"${FORGEJO_URL}/api/v1/repos/{owner}/{repo}/issues?since=2024-06-01T00:00:00Z"
# Issues created by a specific user, sorted by most comments
curl -s \
-H "Authorization: token ${FORGEJO_PAT}" \
"${FORGEJO_URL}/api/v1/repos/{owner}/{repo}/issues?created_by={username}&sort=mostcomment"
# Issues assigned to a user
curl -s \
-H "Authorization: token ${FORGEJO_PAT}" \
"${FORGEJO_URL}/api/v1/repos/{owner}/{repo}/issues?assigned_by={username}"
# Combine multiple filters
curl -s \
-H "Authorization: token ${FORGEJO_PAT}" \
"${FORGEJO_URL}/api/v1/repos/{owner}/{repo}/issues?state=open&labels=bug&milestones=v2.0&sort=priority&limit=50"
Create an Issue
POST /repos/{owner}/{repo}/issues
Parameters
| Parameter | Type | In | Description |
|---|---|---|---|
owner |
string | path | Required. Repository owner |
repo |
string | path | Required. Repository name |
Request Body — CreateIssueOption
| Field | Type | Required | Description |
|---|---|---|---|
title |
string | Yes | Issue title |
body |
string | No | Issue body (Markdown supported) |
assignees |
array of strings | No | Usernames to assign |
milestone |
integer | No | Milestone ID to associate |
labels |
array of integers | No | Label IDs to apply |
closed |
boolean | No | If true, create the issue already closed |
due_date |
string | No | Due date in ISO 8601 format (e.g. 2025-12-31T00:00:00Z) |
ref |
string | No | Branch reference for this issue |
Response
Returns 201 with the created Issue object.
Examples
# Create a simple issue
curl -s -X POST \
-H "Authorization: token ${FORGEJO_PAT}" \
-H "Content-Type: application/json" \
-d '{
"title": "Fix login page redirect"
}' \
"${FORGEJO_URL}/api/v1/repos/{owner}/{repo}/issues"
# Create issue with full details
curl -s -X POST \
-H "Authorization: token ${FORGEJO_PAT}" \
-H "Content-Type: application/json" \
-d '{
"title": "Implement OAuth2 support",
"body": "## Description\n\nWe need to add OAuth2 authentication.\n\n## Tasks\n- [ ] Add OAuth2 provider config\n- [ ] Implement callback handler\n- [ ] Update login page",
"assignees": ["{assignee1}", "{assignee2}"],
"milestone": 3,
"labels": [1, 5, 12],
"due_date": "2025-03-01T00:00:00Z",
"ref": "feature/oauth2"
}' \
"${FORGEJO_URL}/api/v1/repos/{owner}/{repo}/issues"
# Create an already-closed issue (useful for record-keeping)
curl -s -X POST \
-H "Authorization: token ${FORGEJO_PAT}" \
-H "Content-Type: application/json" \
-d '{
"title": "Legacy: migrated from old tracker #4521",
"body": "This issue was migrated from the old tracker and is already resolved.",
"closed": true
}' \
"${FORGEJO_URL}/api/v1/repos/{owner}/{repo}/issues"
Get a Single Issue
GET /repos/{owner}/{repo}/issues/{index}
Parameters
| Parameter | Type | In | Description |
|---|---|---|---|
owner |
string | path | Required. Repository owner |
repo |
string | path | Required. Repository name |
index |
integer | path | Required. Issue index number (the # number shown in the UI) |
Response
Returns 200 with an Issue object. Returns 404 if not found.
Key fields in the Issue response object
| Field | Type | Description |
|---|---|---|
id |
integer | Internal database ID |
number |
integer | Issue index (same as index param) |
title |
string | Issue title |
body |
string | Issue body (Markdown) |
state |
string | open or closed |
labels |
array | Label objects |
milestone |
object | Milestone object (or null) |
assignees |
array | Assignee user objects |
user |
object | Creator user object |
comments |
integer | Number of comments |
created_at |
string | ISO 8601 creation timestamp |
updated_at |
string | ISO 8601 last update timestamp |
closed_at |
string | ISO 8601 close timestamp (or null) |
due_date |
string | ISO 8601 due date (or null) |
pull_request |
object | Present if this is a PR (null for issues) |
ref |
string | Branch reference |
pin_order |
integer | Pin position (0 if not pinned) |
is_locked |
boolean | Whether the issue is locked |
original_author |
string | Original author name (for migrated issues) |
original_author_id |
integer | Original author ID (for migrated issues) |
Examples
# Get issue #42
curl -s \
-H "Authorization: token ${FORGEJO_PAT}" \
"${FORGEJO_URL}/api/v1/repos/{owner}/{repo}/issues/{index}"
# Get issue and extract specific fields with jq
curl -s \
-H "Authorization: token ${FORGEJO_PAT}" \
"${FORGEJO_URL}/api/v1/repos/{owner}/{repo}/issues/{index}" | jq '{title, state, assignees: [.assignees[].login], labels: [.labels[].name]}'
Edit an Issue
PATCH /repos/{owner}/{repo}/issues/{index}
Parameters
| Parameter | Type | In | Description |
|---|---|---|---|
owner |
string | path | Required. Repository owner |
repo |
string | path | Required. Repository name |
index |
integer | path | Required. Issue index number |
Request Body — EditIssueOption
All fields are optional. Only include fields you want to change.
| Field | Type | Description |
|---|---|---|
title |
string | New title |
body |
string | New body (Markdown) |
assignees |
array of strings | New assignee usernames (replaces all current assignees) |
milestone |
integer | New milestone ID. Set to 0 to remove milestone |
state |
string | "open" or "closed" |
due_date |
string | New due date (ISO 8601) |
unset_due_date |
boolean | If true, removes the due date |
ref |
string | New branch reference |
updated_at |
string | Override the updated timestamp (ISO 8601). Useful for migrations |
Response
Returns 201 with the updated Issue object.
Examples
# Update issue title
curl -s -X PATCH \
-H "Authorization: token ${FORGEJO_PAT}" \
-H "Content-Type: application/json" \
-d '{
"title": "Fix login page redirect (updated)"
}' \
"${FORGEJO_URL}/api/v1/repos/{owner}/{repo}/issues/{index}"
# Close an issue
curl -s -X PATCH \
-H "Authorization: token ${FORGEJO_PAT}" \
-H "Content-Type: application/json" \
-d '{
"state": "closed"
}' \
"${FORGEJO_URL}/api/v1/repos/{owner}/{repo}/issues/{index}"
# Reopen an issue
curl -s -X PATCH \
-H "Authorization: token ${FORGEJO_PAT}" \
-H "Content-Type: application/json" \
-d '{
"state": "open"
}' \
"${FORGEJO_URL}/api/v1/repos/{owner}/{repo}/issues/{index}"
# Update body, assignees, and milestone
curl -s -X PATCH \
-H "Authorization: token ${FORGEJO_PAT}" \
-H "Content-Type: application/json" \
-d '{
"body": "## Updated Description\n\nThis is the revised description.",
"assignees": ["{assignee1}", "{assignee2}"],
"milestone": 5
}' \
"${FORGEJO_URL}/api/v1/repos/{owner}/{repo}/issues/{index}"
# Set a due date
curl -s -X PATCH \
-H "Authorization: token ${FORGEJO_PAT}" \
-H "Content-Type: application/json" \
-d '{
"due_date": "2025-06-15T00:00:00Z"
}' \
"${FORGEJO_URL}/api/v1/repos/{owner}/{repo}/issues/{index}"
# Remove a due date
curl -s -X PATCH \
-H "Authorization: token ${FORGEJO_PAT}" \
-H "Content-Type: application/json" \
-d '{
"unset_due_date": true
}' \
"${FORGEJO_URL}/api/v1/repos/{owner}/{repo}/issues/{index}"
# Remove milestone (set to 0)
curl -s -X PATCH \
-H "Authorization: token ${FORGEJO_PAT}" \
-H "Content-Type: application/json" \
-d '{
"milestone": 0
}' \
"${FORGEJO_URL}/api/v1/repos/{owner}/{repo}/issues/{index}"
# Change branch reference
curl -s -X PATCH \
-H "Authorization: token ${FORGEJO_PAT}" \
-H "Content-Type: application/json" \
-d '{
"ref": "{branch}"
}' \
"${FORGEJO_URL}/api/v1/repos/{owner}/{repo}/issues/{index}"
Delete an Issue
DELETE /repos/{owner}/{repo}/issues/{index}
Note: This permanently deletes the issue. Requires admin permissions on the repository. In most cases, prefer closing an issue instead.
Parameters
| Parameter | Type | In | Description |
|---|---|---|---|
owner |
string | path | Required. Repository owner |
repo |
string | path | Required. Repository name |
index |
integer | path | Required. Issue index number |
Response
Returns 204 (No Content) on success.
Example
# Delete issue #42 (admin only, irreversible)
curl -s -X DELETE \
-H "Authorization: token ${FORGEJO_PAT}" \
"${FORGEJO_URL}/api/v1/repos/{owner}/{repo}/issues/{index}"
Pinned Issues
List Pinned Issues
GET /repos/{owner}/{repo}/issues/pinned
Returns issues that are pinned in the repository, ordered by their pin position.
curl -s \
-H "Authorization: token ${FORGEJO_PAT}" \
"${FORGEJO_URL}/api/v1/repos/{owner}/{repo}/issues/pinned"
Pin an Issue
POST /repos/{owner}/{repo}/issues/{index}/pin
Pins the issue to the repository. Requires write access.
curl -s -X POST \
-H "Authorization: token ${FORGEJO_PAT}" \
"${FORGEJO_URL}/api/v1/repos/{owner}/{repo}/issues/{index}/pin"
Returns 204 on success.
Unpin an Issue
DELETE /repos/{owner}/{repo}/issues/{index}/pin
Removes the pin from an issue.
curl -s -X DELETE \
-H "Authorization: token ${FORGEJO_PAT}" \
"${FORGEJO_URL}/api/v1/repos/{owner}/{repo}/issues/{index}/pin"
Returns 204 on success.
Move a Pinned Issue
PATCH /repos/{owner}/{repo}/issues/{index}/pin/{position}
Moves a pinned issue to a new position.
| Parameter | Type | In | Description |
|---|---|---|---|
index |
integer | path | Required. Issue index |
position |
integer | path | Required. New position (1-based) |
# Move pinned issue #42 to position 1 (top)
curl -s -X PATCH \
-H "Authorization: token ${FORGEJO_PAT}" \
"${FORGEJO_URL}/api/v1/repos/{owner}/{repo}/issues/{index}/pin/{position}"
Returns 204 on success.
Issue Subscriptions
List Subscribers
GET /repos/{owner}/{repo}/issues/{index}/subscriptions
Returns users subscribed to the issue.
| Parameter | Type | In | Description |
|---|---|---|---|
page |
integer | query | Page number. Default: 1 |
limit |
integer | query | Page size |
curl -s \
-H "Authorization: token ${FORGEJO_PAT}" \
"${FORGEJO_URL}/api/v1/repos/{owner}/{repo}/issues/{index}/subscriptions"
Check if Current User is Subscribed
GET /repos/{owner}/{repo}/issues/{index}/subscriptions/check
Returns a WatchInfo object with subscribed (boolean), ignored (boolean), reason, created_at, and url.
curl -s \
-H "Authorization: token ${FORGEJO_PAT}" \
"${FORGEJO_URL}/api/v1/repos/{owner}/{repo}/issues/{index}/subscriptions/check"
Subscribe a User
PUT /repos/{owner}/{repo}/issues/{index}/subscriptions/{user}
Subscribes a user to the issue. You can subscribe yourself or others (with appropriate permissions).
| Parameter | Type | In | Description |
|---|---|---|---|
user |
string | path | Required. Username to subscribe |
# Subscribe user "{username}" to issue #42
curl -s -X PUT \
-H "Authorization: token ${FORGEJO_PAT}" \
"${FORGEJO_URL}/api/v1/repos/{owner}/{repo}/issues/{index}/subscriptions/{username}"
Returns 200 on success, 304 if already subscribed.
Unsubscribe a User
DELETE /repos/{owner}/{repo}/issues/{index}/subscriptions/{user}
# Unsubscribe user "{username}" from issue #42
curl -s -X DELETE \
-H "Authorization: token ${FORGEJO_PAT}" \
"${FORGEJO_URL}/api/v1/repos/{owner}/{repo}/issues/{index}/subscriptions/{username}"
Returns 200 on success, 304 if not subscribed.
Issue Timeline
GET /repos/{owner}/{repo}/issues/{index}/timeline
Returns a chronological list of all events on an issue (comments, label changes, assignments, state changes, references, etc.).
Parameters
| Parameter | Type | In | Description |
|---|---|---|---|
owner |
string | path | Required. Repository owner |
repo |
string | path | Required. Repository name |
index |
integer | path | Required. Issue index |
since |
string | query | Only show events after this date (ISO 8601) |
before |
string | query | Only show events before this date (ISO 8601) |
page |
integer | query | Page number. Default: 1 |
limit |
integer | query | Page size |
Timeline Event Types
The type field in each event indicates what happened:
| Type | Description |
|---|---|
comment |
A comment was posted |
reopen |
Issue was reopened |
close |
Issue was closed |
label |
Label was added or removed |
milestone |
Milestone was changed |
assignees |
Assignees were changed |
change_title |
Title was changed |
delete_branch |
Associated branch was deleted |
commit_ref |
A commit referenced this issue |
pull_ref |
A pull request referenced this issue |
comment_ref |
A comment referenced this issue |
issue_ref |
Another issue referenced this one |
change_ref |
Branch reference was changed |
head_ref |
Head reference was changed |
due_date |
Due date was added, changed, or removed |
pin |
Issue was pinned |
unpin |
Issue was unpinned |
lock |
Issue was locked |
unlock |
Issue was unlocked |
project_board |
Issue was added/moved in project board |
added_deadline |
Deadline added |
modified_deadline |
Deadline changed |
removed_deadline |
Deadline removed |
review_request |
Review was requested |
merge |
PR was merged |
code |
Code change event |
Examples
# Get full timeline for issue #42
curl -s \
-H "Authorization: token ${FORGEJO_PAT}" \
"${FORGEJO_URL}/api/v1/repos/{owner}/{repo}/issues/{index}/timeline"
# Get timeline events since a specific date
curl -s \
-H "Authorization: token ${FORGEJO_PAT}" \
"${FORGEJO_URL}/api/v1/repos/{owner}/{repo}/issues/{index}/timeline?since=2024-06-01T00:00:00Z"
# Get timeline with pagination
curl -s \
-H "Authorization: token ${FORGEJO_PAT}" \
"${FORGEJO_URL}/api/v1/repos/{owner}/{repo}/issues/{index}/timeline?page=1&limit=50"
# Extract just state-change events with jq
curl -s \
-H "Authorization: token ${FORGEJO_PAT}" \
"${FORGEJO_URL}/api/v1/repos/{owner}/{repo}/issues/{index}/timeline" \
| jq '[.[] | select(.type == "close" or .type == "reopen")]'