g1t/apps/docs/src/content/docs/reference/api.md

263 lines11,872 bytesCodeBlame
1---
2title: API
3description: Authentication, errors and the endpoints of the REST API.
4---
5
6The REST API lives at `https://api.g1t.sh`. It exposes the same operations as
7the [MCP server](/reference/mcp/).
8
9This page is an overview. The [API reference](/api/reference/) lists
10every endpoint with its parameters and lets you call them from the page. The
11machine-readable description is at
12[api.g1t.sh/openapi.json](https://api.g1t.sh/openapi.json).
13
14## Start at the root
15
16The API is public. Anything you could see on the site without signing in,
17you can read without a token. `GET https://api.g1t.sh/` returns where
18everything is, as URL templates:
19
20```sh
21curl https://api.g1t.sh/
22```
23
24```json
25{
26 "documentation_url": "https://docs.g1t.sh/api/reference/",
27 "current_user_url": "https://api.g1t.sh/user",
28 "repository_url": "https://api.g1t.sh/repos/{owner}/{name}",
29 "issues_url": "https://api.g1t.sh/repos/{owner}/{name}/issues{?state,label}",
30 "pulls_url": "https://api.g1t.sh/repos/{owner}/{name}/pulls{?state}"
31}
32```
33
34## Authentication
35
36A token is needed to change anything, and to see what is private. Send an
37[access token](https://g1t.sh/settings) as a bearer token:
38
39```sh
40curl https://api.g1t.sh/user \
41 -H "Authorization: Bearer $G1T_TOKEN"
42```
43
44Public data can be read without a token. A token that is not valid is
45rejected with `401` rather than treated as anonymous.
46
47## Signing in from a tool
48
49A tool gets a token by having a person approve a short code in their
50browser. See [signing in from a tool](/guides/authentication/#signing-in-from-a-tool).
51
52| Method | Path | |
53| --- | --- | --- |
54| `POST` | `/device/code` | Start a sign-in. Body: `client_name`. |
55| `POST` | `/device/token` | Ask whether it was approved. Body: `device_code`. |
56
57Applications that can open a browser use OAuth instead. See
58[signing in with OAuth](/guides/authentication/#signing-in-with-oauth).
59
60| Method | Path | |
61| --- | --- | --- |
62| `GET` | `/.well-known/oauth-authorization-server` | Where the endpoints are. |
63| `POST` | `/oauth/register` | Register a client. Body: `client_name`, `redirect_uris`. |
64| `POST` | `/oauth/token` | Exchange a code, or refresh. Form-encoded or JSON. |
65
66Accounts are created in a browser only. There is no registration endpoint
67for accounts.
68
69## Errors
70
71Errors are JSON with a stable `code` and a human-readable `message`.
72
73```json
74{ "error": { "code": "not_found", "message": "Repository not found." } }
75```
76
77| Status | Code | Meaning |
78| --- | --- | --- |
79| 401 | `unauthenticated` | A token is required, or the one sent is not valid. |
80| 402 | `payment_required` | The workspace has no agent credit. See [usage and billing](/guides/usage-and-billing/#when-credit-runs-out). |
81| 403 | `forbidden` | You are signed in but not allowed to do this. |
82| 404 | `not_found` | It does not exist, or you cannot see it. |
83| 409 | `conflict` | The request conflicts with the current state. |
84| 422 | `invalid` | The input is not valid. |
85
86## Accounts and repositories
87
88In paths, `{owner}` is the workspace that owns the repository.
89
90| Method | Path | |
91| --- | --- | --- |
92| `GET` | `/user` | Who the token acts as, and the workspaces it can work in. `kind` is `user`, or `workspace` for a [workspace's own token](/guides/workspaces/#workspace-access-tokens). |
93| `POST` | `/workspaces` | Create a workspace. Body: `slug`, `name`. |
94| `GET` | `/repos?q=` | Repositories you can see. |
95| `POST` | `/repos` | Create one. Body: `workspace`, `name`, `description`, `private`, and `import_url` to copy a public repository's default branch. |
96| `GET` | `/repos/{owner}/{name}` | One repository. |
97| `PATCH` | `/repos/{owner}/{name}` | Change it. Body: `description`, `private`, and `protected` to refuse pushes to the default branch. Members only. |
98| `GET` | `/repos/{owner}/{name}/settings` | How it handles pull requests. |
99| `PATCH` | `/repos/{owner}/{name}/settings` | Change that. Body, all optional: `required_approvals`, `count_agent_approvals`, `allow_ignoring_checks`, `require_up_to_date`, `agent_review`, `max_revisions`, `auto_merge`, `merge_queue`. Members only. |
100| `GET` | `/repos/{owner}/{name}/queue` | Its [merge queue](/guides/merge-queue/): entries waiting to land, and those that recently left. |
101| `GET` | `/repos/{owner}/{name}/events?before=` | Its timeline, newest first. |
102
103## Issues
104
105Issues and pull requests share one sequence of numbers per repository.
106
107| Method | Path | |
108| --- | --- | --- |
109| `GET` | `/repos/{owner}/{name}/issues?state=&label=` | Issues, newest first. `state` is `open` or `closed`. |
110| `POST` | `/repos/{owner}/{name}/issues` | Open one. Body: `title`, `body`, `labels`, `checks`. |
111| `GET` | `/repos/{owner}/{name}/issues/{number}` | An issue, its comments and its pull requests. |
112| `PATCH` | `/repos/{owner}/{name}/issues/{number}` | Change `title`, `body` or `labels`. |
113| `POST` | `/repos/{owner}/{name}/issues/{number}/close` | Close. Body: `reason`, `completed` or `not_planned`. |
114| `POST` | `/repos/{owner}/{name}/issues/{number}/reopen` | Reopen. |
115| `POST` | `/repos/{owner}/{name}/plans` | Turn an [outcome](/guides/outcomes/) into a plan. Body: `brief`. Returns `planId`; the plan takes a minute or two to write. Members only. |
116| `GET` | `/repos/{owner}/{name}/plans/{plan}` | The plan: its `status` and the issues it proposes. |
117| `POST` | `/repos/{owner}/{name}/plans/{plan}/apply` | Open its issues. Body: `assign` to put g1t agents on them in dependency order, `keep` to open only some, by position from 1. |
118| `POST` | `/repos/{owner}/{name}/issues/{number}/assign` | Assign it to the [g1t agent](/guides/g1t-agents/), which opens a pull request and sees it through. Body: `instructions` (optional). Returns the pull request. Preview: enabled accounts only. |
119| `POST` | `/repos/{owner}/{name}/issues/{number}/comments` | Comment. Body: `body`. The number may be a pull request's, and then `path` and `line` put the comment on a line of its change. |
120| `GET` | `/repos/{owner}/{name}/labels` | The labels in use. |
121
122```sh
123curl -X POST https://api.g1t.sh/repos/syntaqx/hello/issues \
124 -H "Authorization: Bearer $G1T_TOKEN" \
125 -H "Content-Type: application/json" \
126 -d '{
127 "title": "Greeting should name the caller",
128 "body": "Take a name from the first argument; fall back to world.",
129 "labels": ["feature"],
130 "checks": ["cargo build"]
131 }'
132```
133
134A closed issue says how it was closed. `resolvedBy` is the number of the
135pull request whose merge closed it:
136
137```json
138{
139 "issue": { "number": 12, "state": "closed", "reason": "completed", "resolvedBy": 14 },
140 "pulls": [
141 { "number": 13, "status": "closed", "supersededBy": 14 },
142 { "number": 14, "status": "merged", "mergedBy": "syntaqx" }
143 ],
144 "comments": []
145}
146```
147
148## Pull requests
149
150| Method | Path | |
151| --- | --- | --- |
152| `GET` | `/repos/{owner}/{name}/pulls?state=` | Pull requests, newest first. |
153| `POST` | `/repos/{owner}/{name}/pulls` | Open one. Body: `issue`, `title`, `agent`, and for a branch `branch`, `body`. |
154| `GET` | `/repos/{owner}/{name}/pulls/{number}` | A pull request, its comments and its issue. |
155| `GET` | `/repos/{owner}/{name}/pulls/{number}/changes` | The files it changes, with diffs. |
156| `POST` | `/repos/{owner}/{name}/pulls/{number}/ready` | Mark ready for review. Body: `summary`. |
157| `POST` | `/repos/{owner}/{name}/pulls/{number}/close` | Close without merging. |
158| `POST` | `/repos/{owner}/{name}/pulls/{number}/reviews` | Give a verdict. Body: `verdict` (`approve` or `request_changes`), `body`. Not on your own pull request. |
159| `POST` | `/repos/{owner}/{name}/pulls/{number}/messages` | Send the g1t agent working on it a message. Body: `body`; from a g1t agent, also `kind` and `from_number`. See [talk to agents](/guides/talking-to-agents/). |
160| `POST` | `/repos/{owner}/{name}/pulls/{number}/messages/take` | For a g1t agent at work: the messages it has not seen yet. |
161| `POST` | `/repos/{owner}/{name}/pulls/{number}/merge` | Land it on `main`, or add it to the merge queue where the repository has one on. Body: `keep_issue_open`, `ignore_checks`. Workspace members only; `409` if it is a draft or its checks have not passed. If `main` has moved, the pull request is brought up to date first and lands when that is done: the response is the pull request, still open, and `landing` is true on it until then. A repository that requires pull requests to be up to date answers `409` instead. |
162
163Opening a pull request returns the git remote of its fork:
164
165```json
166{
167 "pull": { "id": "pr_01…", "number": 14, "status": "draft", "issue": 12 },
168 "git": { "remote": "https://g1t.sh/pulls/pr_01….git" }
169}
170```
171
172`title` defaults to the issue's title, and is required when there is no
173`issue`.
174
175Send `branch` to open the pull request from a branch already pushed to the
176repository. No fork is made, `git.remote` is the repository itself, and the
177pull request is `open` at once:
178
179```json
180{
181 "pull": { "number": 15, "status": "open", "branch": "my-change", "fork": null },
182 "git": { "remote": "https://g1t.sh/syntaqx/hello.git" }
183}
184```
185
186Merging a pull request made for an issue closes the issue and records the
187pull request in the issue's `resolvedBy`. Other pull requests for that issue
188that are still a draft or open are closed with `supersededBy` set. Send
189`"keep_issue_open": true` to merge without any of that.
190
191Fetching one pull request also returns:
192
193| Field | |
194| --- | --- |
195| `pull.files` | The files it changes, with lines added and removed. |
196| `overlaps` | Other pull requests in progress changing the same files. |
197| `behind` | Whether `main` has moved since it was made. |
198| `checks` | The latest run of the acceptance checks. |
199| `comments` | Comments and reviews, with `path`, `line` and `verdict`. |
200
201### Checks
202
203A pull request carries `checkStatus`: `queued`, `running`, `passed`, `failed`,
204`errored`, or `null` when no checks have run against its head. Fetching one
205pull request also returns the latest run in full:
206
207```json
208{
209 "pull": { "number": 14, "status": "open", "checkStatus": "failed" },
210 "checks": {
211 "headCommit": "8f3c2e1…",
212 "status": "failed",
213 "results": [
214 { "command": "cargo test", "passed": false, "exitCode": 101, "output": "…", "durationMs": 8420 }
215 ]
216 }
217}
218```
219
220Checks are started by g1t, not through the API. They run when a pull
221request becomes ready for review and again when its head moves.
222
223## Sessions
224
225| Method | Path | |
226| --- | --- | --- |
227| `GET` | `/repos/{owner}/{name}/pulls/{number}/session?after=` | Entries after a sequence number. |
228| `POST` | `/repos/{owner}/{name}/pulls/{number}/session` | Append. Body: `entries`. |
229
230```sh
231curl -X POST https://api.g1t.sh/repos/syntaqx/hello/pulls/14/session \
232 -H "Authorization: Bearer $G1T_TOKEN" \
233 -H "Content-Type: application/json" \
234 -d '{"entries": [{"kind": "message", "text": "Reading src/main.rs."}]}'
235```
236
237Up to 200 entries can be appended per request. See
238[sessions and why-blame](/guides/why-blame/).
239
240## Identifiers and times
241
242Issues and pull requests are addressed by repository and number. Other ids
243are [TypeIDs](https://github.com/jetify-com/typeid): a prefix naming the
244kind of thing, then a UUIDv7 in base32, for example
245`pr_01jb2k7x9hfq0b3zj0f5s2m8ra`. They sort by creation time.
246
247Times are RFC 3339 in UTC, such as `2026-10-01T18:04:11.482Z`.
248
249## Field names
250
251Responses use `camelCase`. Request bodies take the same names as the MCP
252tools, in `snake_case`, and also accept `camelCase`, so you can send back a
253field exactly as you read it:
254
255```sh
256# Both turn off counting agents' approvals.
257curl -X PATCH https://api.g1t.sh/repos/acme/web/settings \
258 -H "Authorization: Bearer g1t_…" -d '{"count_agent_approvals": false}'
259curl -X PATCH https://api.g1t.sh/repos/acme/web/settings \
260 -H "Authorization: Bearer g1t_…" -d '{"countAgentApprovals": false}'
261```
262
263When a body gives a field both ways, the `snake_case` one is used.