Forgejo: REST API for a PR dispatcher
State: open/active · Genre: research
Source: Forgejo’s published API-usage doc (forgejo.org/docs/latest/user/api-usage/) and the live
OpenAPI/swagger document served by Codeberg, a running Forgejo instance
(https://codeberg.org/swagger.v1.json, info.version 16.0.0-dev-753-6bcc6da0+gitea-1.22.0),
fetched 2026-09-25. No Forgejo instance is reachable from this repo — every fact here is read from
published documentation and a live instance’s spec, never exercised against a live API call, so
none of it is confirmed against an actual response body.
- Verified. Token header:
Authorization: token <token>. Quoting the doc: “Forgejo requires the wordtokenbefore the API key.” - Verified. Also accepted: HTTP basic auth,
Authorization: Bearer <oauth-token>, andtoken=/access_token=query parameters. - Verified. Every instance serves its spec at
/api/swagger(interactive) and/swagger.v1.json(the OpenAPI document) — the paths Codeberg uses.
Pull requests
Section titled “Pull requests”| Need | Endpoint |
|---|---|
| List PRs | GET /repos/{owner}/{repo}/pulls |
| Get one PR | GET /repos/{owner}/{repo}/pulls/{index} |
| Patch a PR (title/body/state/etc.) | PATCH /repos/{owner}/{repo}/pulls/{index} |
| Check if merged | GET /repos/{owner}/{repo}/pulls/{index}/merge (204 = merged, 404 = not) |
| Merge it | POST /repos/{owner}/{repo}/pulls/{index}/merge |
| Update head from base | POST /repos/{owner}/{repo}/pulls/{index}/update?style=merge|rebase |
Verified against swagger.v1.json’s paths object. The update endpoint’s style query
parameter is confirmed as an enum of exactly merge/rebase.
Verified (schema fields, definitions.PullRequest.properties): the PullRequest object
carries mergeable (boolean), merged (boolean), merge_base, merge_commit_sha, draft,
state, plus the usual head/base/user/labels. It has no mergeable_state-style enum and
no review_decision field — see Gaps below.
Reviews and review comments
Section titled “Reviews and review comments”| Need | Endpoint |
|---|---|
| List reviews | GET /repos/{owner}/{repo}/pulls/{index}/reviews |
| Submit a review | POST /repos/{owner}/{repo}/pulls/{index}/reviews |
| One review | GET/POST/DELETE /repos/{owner}/{repo}/pulls/{index}/reviews/{id} |
| A review’s comments | GET/POST /repos/{owner}/{repo}/pulls/{index}/reviews/{id}/comments |
Verified against swagger.v1.json. A PullReview carries state, body, dismissed,
official, stale, comments_count, user, submitted_at. state is a named
ReviewStateType string enum; the spec’s own text does not spell out its values. The usual
Gitea/Forgejo values (APPROVED, PENDING, COMMENT, REQUEST_CHANGES, REQUEST_REVIEW) are
inferred, not read from the spec.
Issue comments (a PR is an issue)
Section titled “Issue comments (a PR is an issue)”- Verified. List/post:
GET/POST /repos/{owner}/{repo}/issues/{index}/comments. A PR’s general conversation comments — as opposed to inline review comments — live on its issue number, the same as any other issue.
Commit status and checks
Section titled “Commit status and checks”| Need | Endpoint |
|---|---|
| Combined status for a ref | GET /repos/{owner}/{repo}/commits/{ref}/status |
| Individual statuses for a ref | GET /repos/{owner}/{repo}/commits/{ref}/statuses |
| Post a status | POST /repos/{owner}/{repo}/statuses/{sha} |
Verified against swagger.v1.json. CombinedStatus.state and each CommitStatus.state share
one enum: pending, success, error, failure, warning, skipped.
Verified: Forgejo Actions (/repos/{owner}/{repo}/actions/runs, .../jobs, .../artifacts,
.../workflows/{file}/dispatches, etc.) is a separate resource tree, with no GitHub-style
check-run object tied to a commit. Inferred, not read from the spec: whether a Forgejo Actions
job also posts a commit status (the way Gitea’s own runner does) so it shows up in
.../commits/{ref}/status — the spec documents the two trees separately and does not say one
writes into the other.
Webhooks
Section titled “Webhooks”- Verified. Repo-level:
GET/POST /repos/{owner}/{repo}/hooks; org- and admin-level equivalents also exist.
Gaps vs. GitHub
Section titled “Gaps vs. GitHub”Verified (read solutions/ontological/lib/services/pr/ops/classify.ts): sdlc’s classify.ts
reads four fields off gh’s GraphQL response — mergeable (enum MERGEABLE/CONFLICTING/
UNKNOWN), mergeStateStatus (CLEAN/DIRTY/BEHIND/BLOCKED/UNSTABLE/…),
reviewDecision (APPROVED/CHANGES_REQUESTED/REVIEW_REQUIRED), and statusCheckRollup (a
list of checks, each with a conclusion).
Forgejo has no direct equivalent of three of the four:
mergeableis a plain boolean, not a three-state enum — it collapsesCONFLICTINGand the merge-ineligible half ofUNKNOWNinto onefalse.- Nothing plays the role of
mergeStateStatus. ABEHINDverdict has to be computed by the caller, by comparingmerge_baseagainst the base branch’s current head, not read off one field. - Nothing plays the role of
reviewDecision. It has to be derived from the full review list’sstatevalues, applying whatever “latest review per user wins” rule GitHub’s own field encodes internally. statusCheckRollupmaps reasonably well to combined status’sstatusesarray, modulo the state-vocabulary difference (failure/error/warningvs. GitHub’sconclusionset).
A Forgejo classifier cannot reuse classify.ts’s field reads; it needs its own verdict logic
built from mergeable, a computed behind-check, the review list, and combined status.
Open questions
Section titled “Open questions”- Whether a Forgejo Actions job’s result reaches
commits/{ref}/statusas a normal commit status, or stays confined to the separate Actions endpoints. - The exact
ReviewStateTypeenum values (not given in the spec text). - Everything above is read from documentation and a live instance’s spec, never a live API call — confirming it needs a real Forgejo instance to test against.