Skip to content

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 word token before the API key.”
  • Verified. Also accepted: HTTP basic auth, Authorization: Bearer <oauth-token>, and token=/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.
NeedEndpoint
List PRsGET /repos/{owner}/{repo}/pulls
Get one PRGET /repos/{owner}/{repo}/pulls/{index}
Patch a PR (title/body/state/etc.)PATCH /repos/{owner}/{repo}/pulls/{index}
Check if mergedGET /repos/{owner}/{repo}/pulls/{index}/merge (204 = merged, 404 = not)
Merge itPOST /repos/{owner}/{repo}/pulls/{index}/merge
Update head from basePOST /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.

NeedEndpoint
List reviewsGET /repos/{owner}/{repo}/pulls/{index}/reviews
Submit a reviewPOST /repos/{owner}/{repo}/pulls/{index}/reviews
One reviewGET/POST/DELETE /repos/{owner}/{repo}/pulls/{index}/reviews/{id}
A review’s commentsGET/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.

  • 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.
NeedEndpoint
Combined status for a refGET /repos/{owner}/{repo}/commits/{ref}/status
Individual statuses for a refGET /repos/{owner}/{repo}/commits/{ref}/statuses
Post a statusPOST /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.

  • Verified. Repo-level: GET/POST /repos/{owner}/{repo}/hooks; org- and admin-level equivalents also exist.

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:

  • mergeable is a plain boolean, not a three-state enum — it collapses CONFLICTING and the merge-ineligible half of UNKNOWN into one false.
  • Nothing plays the role of mergeStateStatus. A BEHIND verdict has to be computed by the caller, by comparing merge_base against 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’s state values, applying whatever “latest review per user wins” rule GitHub’s own field encodes internally.
  • statusCheckRollup maps reasonably well to combined status’s statuses array, modulo the state-vocabulary difference (failure/error/warning vs. GitHub’s conclusion set).

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.

  • Whether a Forgejo Actions job’s result reaches commits/{ref}/status as a normal commit status, or stays confined to the separate Actions endpoints.
  • The exact ReviewStateType enum 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.

← Back to Notes