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

181 lines7,058 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](/guides/bring-your-own-agent/).
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## Authentication
15
16Send an [access token](https://g1t.sh/settings) as a bearer token:
17
18```sh
19curl https://api.g1t.sh/v1/user \
20 -H "Authorization: Bearer $G1T_TOKEN"
21```
22
23Public data can be read without a token. A token that is not valid is
24rejected with `401` rather than treated as anonymous.
25
26## Signing in from a tool
27
28A tool gets a token by having a person approve a short code in their
29browser. See [signing in from a tool](/guides/authentication/#signing-in-from-a-tool).
30
31| Method | Path | |
32| --- | --- | --- |
33| `POST` | `/v1/device/code` | Start a sign-in. Body: `client_name`. |
34| `POST` | `/v1/device/token` | Ask whether it was approved. Body: `device_code`. |
35
36Applications that can open a browser use OAuth instead. See
37[signing in with OAuth](/guides/authentication/#signing-in-with-oauth).
38
39| Method | Path | |
40| --- | --- | --- |
41| `GET` | `/.well-known/oauth-authorization-server` | Where the endpoints are. |
42| `POST` | `/oauth/register` | Register a client. Body: `client_name`, `redirect_uris`. |
43| `POST` | `/oauth/token` | Exchange a code, or refresh. Form-encoded or JSON. |
44
45Accounts are created in a browser only. There is no registration endpoint
46for accounts.
47
48## Errors
49
50Errors are JSON with a stable `code` and a human-readable `message`.
51
52```json
53{ "error": { "code": "not_found", "message": "Repository not found." } }
54```
55
56| Status | Code | Meaning |
57| --- | --- | --- |
58| 401 | `unauthenticated` | A token is required, or the one sent is not valid. |
59| 403 | `forbidden` | You are signed in but not allowed to do this. |
60| 404 | `not_found` | It does not exist, or you cannot see it. |
61| 409 | `conflict` | The request conflicts with the current state. |
62| 422 | `invalid` | The input is not valid. |
63
64## Accounts and repositories
65
66In paths, `{owner}` is the workspace that owns the repository.
67
68| Method | Path | |
69| --- | --- | --- |
70| `GET` | `/v1/user` | The account the token belongs to, and its workspaces. |
71| `POST` | `/v1/workspaces` | Create a workspace. Body: `slug`, `name`. |
72| `GET` | `/v1/repos?q=` | Repositories you can see. |
73| `POST` | `/v1/repos` | Create one. Body: `workspace`, `name`, `description`, `private`. |
74| `GET` | `/v1/repos/{owner}/{name}` | One repository. |
75| `GET` | `/v1/repos/{owner}/{name}/events?before=` | Its timeline, newest first. |
76
77## Issues
78
79Issues and pull requests share one sequence of numbers per repository.
80
81| Method | Path | |
82| --- | --- | --- |
83| `GET` | `/v1/repos/{owner}/{name}/issues?state=&label=` | Issues, newest first. `state` is `open` or `closed`. |
84| `POST` | `/v1/repos/{owner}/{name}/issues` | Open one. Body: `title`, `body`, `labels`, `checks`. |
85| `GET` | `/v1/repos/{owner}/{name}/issues/{number}` | An issue, its comments and its pull requests. |
86| `PATCH` | `/v1/repos/{owner}/{name}/issues/{number}` | Change `title`, `body` or `labels`. |
87| `POST` | `/v1/repos/{owner}/{name}/issues/{number}/close` | Close. Body: `reason`, `completed` or `not_planned`. |
88| `POST` | `/v1/repos/{owner}/{name}/issues/{number}/reopen` | Reopen. |
89| `POST` | `/v1/repos/{owner}/{name}/issues/{number}/comments` | Comment. Body: `body`. The number may be a pull request's. |
90| `GET` | `/v1/repos/{owner}/{name}/labels` | The labels in use. |
91
92```sh
93curl -X POST https://api.g1t.sh/v1/repos/syntaqx/hello/issues \
94 -H "Authorization: Bearer $G1T_TOKEN" \
95 -H "Content-Type: application/json" \
96 -d '{
97 "title": "Greeting should name the caller",
98 "body": "Take a name from the first argument; fall back to world.",
99 "labels": ["feature"],
100 "checks": ["cargo build"]
101 }'
102```
103
104A closed issue says how it was closed. `resolvedBy` is the number of the
105pull request whose merge closed it:
106
107```json
108{
109 "issue": { "number": 12, "state": "closed", "reason": "completed", "resolvedBy": 14 },
110 "pulls": [
111 { "number": 13, "status": "closed", "supersededBy": 14 },
112 { "number": 14, "status": "merged", "mergedBy": "syntaqx" }
113 ],
114 "comments": []
115}
116```
117
118## Pull requests
119
120| Method | Path | |
121| --- | --- | --- |
122| `GET` | `/v1/repos/{owner}/{name}/pulls?state=` | Pull requests, newest first. |
123| `POST` | `/v1/repos/{owner}/{name}/pulls` | Open one. Body: `issue`, `title`, `agent`, and for a branch `branch`, `body`. |
124| `GET` | `/v1/repos/{owner}/{name}/pulls/{number}` | A pull request, its comments and its issue. |
125| `GET` | `/v1/repos/{owner}/{name}/pulls/{number}/changes` | The files it changes, with diffs. |
126| `POST` | `/v1/repos/{owner}/{name}/pulls/{number}/ready` | Mark ready for review. Body: `summary`. |
127| `POST` | `/v1/repos/{owner}/{name}/pulls/{number}/close` | Close without merging. |
128| `POST` | `/v1/repos/{owner}/{name}/pulls/{number}/merge` | Land it on `main`. Body: `keep_issue_open`. Workspace members only; `409` if it is a draft or `main` has moved. |
129
130Opening a pull request returns the git remote of its fork:
131
132```json
133{
134 "pull": { "id": "pr_01…", "number": 14, "status": "draft", "issue": 12 },
135 "git": { "remote": "https://g1t.sh/pulls/pr_01….git" }
136}
137```
138
139`title` defaults to the issue's title, and is required when there is no
140`issue`.
141
142Send `branch` to open the pull request from a branch already pushed to the
143repository. No fork is made, `git.remote` is the repository itself, and the
144pull request is `open` at once:
145
146```json
147{
148 "pull": { "number": 15, "status": "open", "branch": "my-change", "fork": null },
149 "git": { "remote": "https://g1t.sh/syntaqx/hello.git" }
150}
151```
152
153Merging a pull request made for an issue closes the issue and records the
154pull request in the issue's `resolvedBy`. Other pull requests for that issue
155that are still a draft or open are closed with `supersededBy` set. Send
156`"keep_issue_open": true` to merge without any of that.
157
158## Sessions
159
160| Method | Path | |
161| --- | --- | --- |
162| `GET` | `/v1/repos/{owner}/{name}/pulls/{number}/session?after=` | Entries after a sequence number. |
163| `POST` | `/v1/repos/{owner}/{name}/pulls/{number}/session` | Append. Body: `entries`. |
164
165```sh
166curl -X POST https://api.g1t.sh/v1/repos/syntaqx/hello/pulls/14/session \
167 -H "Authorization: Bearer $G1T_TOKEN" \
168 -H "Content-Type: application/json" \
169 -d '{"entries": [{"kind": "message", "text": "Reading src/main.rs."}]}'
170```
171
172Up to 200 entries can be appended per request.
173
174## Identifiers and times
175
176Issues and pull requests are addressed by repository and number. Other ids
177are [TypeIDs](https://github.com/jetify-com/typeid): a prefix naming the
178kind of thing, then a UUIDv7 in base32, for example
179`pr_01jb2k7x9hfq0b3zj0f5s2m8ra`. They sort by creation time.
180
181Times are RFC 3339 in UTC, such as `2026-10-01T18:04:11.482Z`.