Tahinli/forge
public0 watching
★ 0
| 1 | # forge JSON API |
| 2 | |
| 3 | A small JSON API under `/api/v1`. It is forge's own; it does not copy another forge's API. |
| 4 | The examples use the base URL from `config/forge.toml`, `http://127.0.0.1:7660`. |
| 5 | |
| 6 | ## Authentication |
| 7 | |
| 8 | Requests authenticate with a personal access token. Create one at **Settings → Tokens** |
| 9 | (`/settings/tokens`). Send it either way: |
| 10 | |
| 11 | ``` |
| 12 | Authorization: Bearer forge_... |
| 13 | Authorization: token forge_... |
| 14 | ``` |
| 15 | |
| 16 | - A token needs the `repo` scope. It is the only scope the token form offers. A token without it |
| 17 | gets `403`. |
| 18 | - Session cookies are never read by this API. A browser that is logged in cannot call these |
| 19 | routes by itself, so CSRF does not apply. Without a token, a write gets `401`. |
| 20 | - Anonymous reads of public repos need no token. Anonymous calls that cannot read a repo get |
| 21 | `401`, as git does, so a private repo looks the same as a missing one to an anonymous caller. |
| 22 | A signed-in user with no access to a repo gets `404`. |
| 23 | - Failed authentication counts against the client IP. It shares the budget git authentication |
| 24 | uses, so after too many failures the API answers `429` with `Retry-After`. |
| 25 | |
| 26 | Errors are JSON: `{"error": "message"}`. |
| 27 | |
| 28 | | Status | Meaning | |
| 29 | | --- | --- | |
| 30 | | 400 | The body is not valid JSON. | |
| 31 | | 401 | No token, an unknown or expired token, or an anonymous caller who cannot use the repo. | |
| 32 | | 403 | The token lacks the `repo` scope, or the user has read-only access and tried to write. | |
| 33 | | 404 | The repo or the commit does not exist, or the signed-in user cannot see the repo. | |
| 34 | | 413 | The request body is larger than 16 KiB. | |
| 35 | | 422 | A field has an invalid value, or `sha` is not a full commit id. | |
| 36 | | 429 | Too many failed authentication attempts from this IP. | |
| 37 | |
| 38 | ## Commit statuses |
| 39 | |
| 40 | A status reports the result of one check (for example a CI job) on one commit. Each commit can |
| 41 | have one status per **context**. Reporting again for the same context replaces the earlier |
| 42 | report, so the latest report wins. |
| 43 | |
| 44 | A status has these fields: |
| 45 | |
| 46 | | Field | Notes | |
| 47 | | --- | --- | |
| 48 | | `state` | `pending`, `success`, `failure` or `error`. | |
| 49 | | `context` | Name of the check, 1 to 255 characters. Defaults to `default`. | |
| 50 | | `description` | Optional, at most 1024 characters. | |
| 51 | | `target_url` | Optional, `http` or `https` only, at most 2048 bytes. Shown as a link. | |
| 52 | |
| 53 | The combined state of a commit is worked out from every context: |
| 54 | |
| 55 | - any `failure` or `error` gives `failure`; |
| 56 | - otherwise any `pending` gives `pending`; |
| 57 | - otherwise `success`; |
| 58 | - a commit with no statuses has `state: null`. |
| 59 | |
| 60 | ### Report a status |
| 61 | |
| 62 | `POST /api/v1/repos/{owner}/{repo}/statuses/{sha}` needs a token with write access to the repo. |
| 63 | `sha` is the full 40-character (or 64-character, SHA-256) commit id. Upper-case hex is accepted. |
| 64 | |
| 65 | ```sh |
| 66 | curl -X POST http://127.0.0.1:7660/api/v1/repos/alice/demo/statuses/$SHA \ |
| 67 | -H "Authorization: Bearer $FORGE_TOKEN" \ |
| 68 | -H "Content-Type: application/json" \ |
| 69 | -d '{"state": "success", "context": "ci/build", "description": "Passed in 42s", |
| 70 | "target_url": "https://ci.example.com/runs/7"}' |
| 71 | ``` |
| 72 | |
| 73 | Response, `201 Created`: |
| 74 | |
| 75 | ```json |
| 76 | { |
| 77 | "id": "01J...", |
| 78 | "sha": "0123...", |
| 79 | "context": "ci/build", |
| 80 | "state": "success", |
| 81 | "description": "Passed in 42s", |
| 82 | "target_url": "https://ci.example.com/runs/7", |
| 83 | "created_at": "2026-10-15T10:00:00Z", |
| 84 | "updated_at": "2026-10-15T10:00:00Z" |
| 85 | } |
| 86 | ``` |
| 87 | |
| 88 | Only `state` is required. A commit that is not in the repo gets `404`. |
| 89 | |
| 90 | ### Read the statuses of a commit |
| 91 | |
| 92 | `GET /api/v1/repos/{owner}/{repo}/commits/{sha}/statuses` needs no token for a public repo. A |
| 93 | token is needed for a private one. |
| 94 | |
| 95 | ```sh |
| 96 | curl http://127.0.0.1:7660/api/v1/repos/alice/demo/commits/$SHA/statuses \ |
| 97 | -H "Authorization: Bearer $FORGE_TOKEN" |
| 98 | ``` |
| 99 | |
| 100 | Response, `200 OK`: |
| 101 | |
| 102 | ```json |
| 103 | { |
| 104 | "state": "failure", |
| 105 | "statuses": [ |
| 106 | { |
| 107 | "id": "01J...", |
| 108 | "sha": "0123...", |
| 109 | "context": "ci/build", |
| 110 | "state": "failure", |
| 111 | "description": "2 tests failed", |
| 112 | "target_url": "https://ci.example.com/runs/8", |
| 113 | "created_at": "2026-10-15T10:05:00Z", |
| 114 | "updated_at": "2026-10-15T10:05:00Z" |
| 115 | }, |
| 116 | { |
| 117 | "id": "01J...", |
| 118 | "sha": "0123...", |
| 119 | "context": "lint", |
| 120 | "state": "success", |
| 121 | "description": null, |
| 122 | "target_url": null, |
| 123 | "created_at": "2026-10-15T10:01:00Z", |
| 124 | "updated_at": "2026-10-15T10:01:00Z" |
| 125 | } |
| 126 | ] |
| 127 | } |
| 128 | ``` |
| 129 | |
| 130 | `statuses` is ordered by `context`. The list is empty and `state` is `null` when nothing has |
| 131 | reported on the commit. |