Skip to content

Issue & Project Metadata Automation

Use this guide to build repeatable triage workflows for issues.

A typical triage flow needs two kinds of operations:

  • Label management — read and write issue labels
  • Project card management — place or move the issue on a project board

Treat Forgejo Proxy as the canonical HTTP surface for both.

  • Native Forgejo resources are available through the proxy under /api/v1/...
  • Proxy-only project resources are also under /api/v1/...
  • Root-path exceptions stay narrow and are not part of this workflow

The practical result is that your scripts can usually use one base URL: FORGEJO_PROXY_URL.

In the forgejo-proxy org, triage labels are maintained as org-level labels such as Kind/*, Priority/*, and Reviewed/*.

Terminal window
thfg label list-org --org forgejo-proxy
Terminal window
curl -s \
-H "Authorization: token $FORGEJO_TOKEN" \
"$FORGEJO_PROXY_URL/api/v1/orgs/forgejo-proxy/labels" \
| jq '.[] | {id, name, color}'

Example output:

{ "id": 12, "name": "Kind/Bug", "color": "#d73a4a" }
{ "id": 13, "name": "Kind/Feature", "color": "#a2eeef" }
{ "id": 14, "name": "Priority/High", "color": "#e4e669" }
Terminal window
# CLI
thfg issue label-set forgejo-proxy/forgejo-proxy 7 \
--label "Kind/Bug" \
--label "Priority/High"
# curl via proxy /api/v1
curl -s -X PUT \
-H "Authorization: token $FORGEJO_TOKEN" \
-H "Content-Type: application/json" \
-d '{"labels":[12,14]}' \
"$FORGEJO_PROXY_URL/api/v1/repos/forgejo-proxy/forgejo-proxy/issues/7/labels"
Terminal window
# CLI
thfg issue label-add forgejo-proxy/forgejo-proxy 7 --label "Reviewed/Yes"
# curl via proxy /api/v1
curl -s -X POST \
-H "Authorization: token $FORGEJO_TOKEN" \
-H "Content-Type: application/json" \
-d '{"labels":[16]}' \
"$FORGEJO_PROXY_URL/api/v1/repos/forgejo-proxy/forgejo-proxy/issues/7/labels"
Terminal window
BUG_ID=$(thfg resolve label "Kind/Bug" --org forgejo-proxy)
HIGH_ID=$(thfg resolve label "Priority/High" --org forgejo-proxy)

Project automation is now documented with the normalized /api/v1/projects/... contract.

Terminal window
thfg project list --org forgejo-proxy --output-format json | jq '.[] | {id, title}'
thfg project columns --id 2 --output-format json | jq '.[] | {id, title}'
Terminal window
# List projects in the org
curl -s \
-H "Authorization: token $FORGEJO_TOKEN" \
-H "X-Webui-Username: $FORGEJO_WEBUI_USERNAME" \
-H "X-Webui-Password: $FORGEJO_WEBUI_PASSWORD" \
"$FORGEJO_PROXY_URL/api/v1/orgs/forgejo-proxy/projects" \
| jq '.[] | {id, title}'
# List columns in project 2
curl -s \
-H "Authorization: token $FORGEJO_TOKEN" \
-H "X-Webui-Username: $FORGEJO_WEBUI_USERNAME" \
-H "X-Webui-Password: $FORGEJO_WEBUI_PASSWORD" \
"$FORGEJO_PROXY_URL/api/v1/projects/2/columns" \
| jq '.[] | {id, title}'

The normalized raw API is column-scoped:

POST /api/v1/projects/{project_id}/columns/{column_id}/cards

Use it both to place a new issue into a project and to move an existing card to another column.

Terminal window
# Add the issue using the CLI convenience command
thfg project add-issue 2 forgejo-proxy/forgejo-proxy#7
# Move the card to In Progress (column 7)
thfg project move-card 2 7 forgejo-proxy/forgejo-proxy#7
Terminal window
curl -s -X POST \
-H "Authorization: token $FORGEJO_TOKEN" \
-H "X-Webui-Username: $FORGEJO_WEBUI_USERNAME" \
-H "X-Webui-Password: $FORGEJO_WEBUI_PASSWORD" \
-H "Content-Type: application/json" \
-d '{"issue_id":7,"owner":"forgejo-proxy","repo":"forgejo-proxy"}' \
"$FORGEJO_PROXY_URL/api/v1/projects/2/columns/5/cards"
Terminal window
curl -s -X POST \
-H "Authorization: token $FORGEJO_TOKEN" \
-H "X-Webui-Username: $FORGEJO_WEBUI_USERNAME" \
-H "X-Webui-Password: $FORGEJO_WEBUI_PASSWORD" \
-H "Content-Type: application/json" \
-d '{"issue_id":7,"owner":"forgejo-proxy","repo":"forgejo-proxy"}' \
"$FORGEJO_PROXY_URL/api/v1/projects/2/columns/7/cards"
#!/usr/bin/env bash
set -euo pipefail
ORG="forgejo-proxy"
REPO="forgejo-proxy/forgejo-proxy"
ISSUE=42
PROJECT_ID=2
TARGET_COLUMN_ID=7
BUG_ID=$(thfg resolve label "Kind/Bug" --org "$ORG")
HIGH_ID=$(thfg resolve label "Priority/High" --org "$ORG")
thfg issue label-set "$REPO" "$ISSUE" \
--label "$BUG_ID" \
--label "$HIGH_ID"
thfg project add-issue "$PROJECT_ID" "$REPO#$ISSUE"
thfg project move-card "$PROJECT_ID" "$TARGET_COLUMN_ID" "$REPO#$ISSUE"
VariableRequired for
FORGEJO_PROXY_URLAll curl examples in this guide
FORGEJO_TOKEN or FORGEJO_API_TOKENAuthenticated API access
FORGEJO_WEBUI_USERNAME / FORGEJO_WEBUI_PASSWORDRaw project API calls and other WebUI-backed operations

For CLI usage, the same values usually come from your active thfg host entry:

  • proxyUrl
  • token
  • username
  • password

If a script still calls /api/v1/proxy/..., update it to the normalized /api/v1/... contract before debugging anything else.

Older scripts sometimes relied on the proxy choosing a default project column implicitly. The normalized raw API is more explicit: choose the target column yourself and call the column-scoped cards endpoint.

Org labels are the authoritative triage labels

Section titled “Org labels are the authoritative triage labels”

If triage depends on org-level labels, resolve those label IDs from the org and reuse them. Do not create repo-local duplicates just because a repo endpoint accepts writes.