# forge JSON API A small JSON API under `/api/v1`. It is forge's own; it does not copy another forge's API. The examples use the base URL from `config/forge.toml`, `http://127.0.0.1:7660`. ## Authentication Requests authenticate with a personal access token. Create one at **Settings → Tokens** (`/settings/tokens`). Send it either way: ``` Authorization: Bearer forge_... Authorization: token forge_... ``` - A token needs the `repo` scope. It is the only scope the token form offers. A token without it gets `403`. - Session cookies are never read by this API. A browser that is logged in cannot call these routes by itself, so CSRF does not apply. Without a token, a write gets `401`. - Anonymous reads of public repos need no token. Anonymous calls that cannot read a repo get `401`, as git does, so a private repo looks the same as a missing one to an anonymous caller. A signed-in user with no access to a repo gets `404`. - Failed authentication counts against the client IP. It shares the budget git authentication uses, so after too many failures the API answers `429` with `Retry-After`. Errors are JSON: `{"error": "message"}`. | Status | Meaning | | --- | --- | | 400 | The body is not valid JSON. | | 401 | No token, an unknown or expired token, or an anonymous caller who cannot use the repo. | | 403 | The token lacks the `repo` scope, or the user has read-only access and tried to write. | | 404 | The repo or the commit does not exist, or the signed-in user cannot see the repo. | | 413 | The request body is larger than 16 KiB. | | 422 | A field has an invalid value, or `sha` is not a full commit id. | | 429 | Too many failed authentication attempts from this IP. | ## Commit statuses A status reports the result of one check (for example a CI job) on one commit. Each commit can have one status per **context**. Reporting again for the same context replaces the earlier report, so the latest report wins. A status has these fields: | Field | Notes | | --- | --- | | `state` | `pending`, `success`, `failure` or `error`. | | `context` | Name of the check, 1 to 255 characters. Defaults to `default`. | | `description` | Optional, at most 1024 characters. | | `target_url` | Optional, `http` or `https` only, at most 2048 bytes. Shown as a link. | The combined state of a commit is worked out from every context: - any `failure` or `error` gives `failure`; - otherwise any `pending` gives `pending`; - otherwise `success`; - a commit with no statuses has `state: null`. ### Report a status `POST /api/v1/repos/{owner}/{repo}/statuses/{sha}` needs a token with write access to the repo. `sha` is the full 40-character (or 64-character, SHA-256) commit id. Upper-case hex is accepted. ```sh curl -X POST http://127.0.0.1:7660/api/v1/repos/alice/demo/statuses/$SHA \ -H "Authorization: Bearer $FORGE_TOKEN" \ -H "Content-Type: application/json" \ -d '{"state": "success", "context": "ci/build", "description": "Passed in 42s", "target_url": "https://ci.example.com/runs/7"}' ``` Response, `201 Created`: ```json { "id": "01J...", "sha": "0123...", "context": "ci/build", "state": "success", "description": "Passed in 42s", "target_url": "https://ci.example.com/runs/7", "created_at": "2026-10-15T10:00:00Z", "updated_at": "2026-10-15T10:00:00Z" } ``` Only `state` is required. A commit that is not in the repo gets `404`. ### Read the statuses of a commit `GET /api/v1/repos/{owner}/{repo}/commits/{sha}/statuses` needs no token for a public repo. A token is needed for a private one. ```sh curl http://127.0.0.1:7660/api/v1/repos/alice/demo/commits/$SHA/statuses \ -H "Authorization: Bearer $FORGE_TOKEN" ``` Response, `200 OK`: ```json { "state": "failure", "statuses": [ { "id": "01J...", "sha": "0123...", "context": "ci/build", "state": "failure", "description": "2 tests failed", "target_url": "https://ci.example.com/runs/8", "created_at": "2026-10-15T10:05:00Z", "updated_at": "2026-10-15T10:05:00Z" }, { "id": "01J...", "sha": "0123...", "context": "lint", "state": "success", "description": null, "target_url": null, "created_at": "2026-10-15T10:01:00Z", "updated_at": "2026-10-15T10:01:00Z" } ] } ``` `statuses` is ordered by `context`. The list is empty and `state` is `null` when nothing has reported on the commit.