markdown · 5679 bytes Raw Blame History

Repositories

The repository core REST surface. Mirrors GitHub's shape where the model matches; deviations are called out inline. List routes emit the standard Link: pagination header (see overview).

Repo shape

Every list and single-repo response renders the same envelope:

{
  "id": 12,
  "name": "demo",
  "full_name": "alice/demo",
  "owner_login": "alice",
  "owner_type": "user",
  "description": "first cut",
  "visibility": "public",
  "private": false,
  "default_branch": "trunk",
  "fork": false,
  "archived": false,
  "has_issues": true,
  "has_pulls": true,
  "star_count": 0,
  "watcher_count": 0,
  "fork_count": 0,
  "created_at": "2026-05-12T04:00:00Z",
  "updated_at": "2026-05-12T04:00:00Z"
}

owner_type is "user" or "org". default_branch is shithub's own default name (trunk), not GitHub's main — read this field; don't hardcode.

List the authenticated user's repos

GET /api/v1/user/repos

Required scope: repo:read. Returns every repo the user owns, private included. Paginated; ?per_page= (≤100) and ?page=.

List a user's public repos

GET /api/v1/users/{username}/repos

Required scope: repo:read. Returns the named user's public repos. When the authenticated viewer is the named user, this endpoint returns the same set as GET /api/v1/user/repos (private included) to match the gh-shape.

List an org's repos

GET /api/v1/orgs/{org}/repos

Required scope: repo:read. Org members see every repo (visibility-aware). Non-members see only public repos.

Get a single repo

GET /api/v1/repos/{owner}/{repo}

Required scope: repo:read. Returns the repo envelope above. 404 when the caller can't see the repo (existence-leak-safe treatment for private repos belonging to someone else).

Create a personal repo

POST /api/v1/user/repos

Required scope: repo:write. Body fields (all optional except name):

{
  "name": "demo",
  "description": "first cut",
  "visibility": "public",
  "auto_init": true,
  "license_template": "mit",
  "gitignore_template": "Go"
}
Field Type Notes
name string Lowercased, [a-z0-9._-], 1–99 chars.
description string ≤350 chars.
visibility string "public" or "private". Defaults to "private".
private bool gh-compatible alternative to visibility.
auto_init bool Seed an initial README commit.
license_template string License key (e.g. mit); requires auto_init=true.
gitignore_template string Gitignore preset name; requires auto_init=true.

Returns 201 with the repo envelope.

Errors

Status When
401 PAT missing/invalid.
403 PAT lacks repo:write scope.
409 Name already taken for this owner.
422 Invalid name, description too long, unknown license/gitignore template, or actor lacks a verified primary email.

Create an org repo

POST /api/v1/orgs/{org}/repos

Required scope: repo:write. Same body shape as the personal variant. Org members can create only when the org has allow_member_repo_create enabled; owners (and site admins) bypass that gate. Non-members get 404 (existence-leak-safe).

Update repo settings

PATCH /api/v1/repos/{owner}/{repo}

Required scope: repo:write. Only the fields you send are modified; everything else stays as it was.

{
  "description": "new copy",
  "has_issues": false,
  "has_pulls": true,
  "archived": true,
  "visibility": "private"
}

Setting archived flips the archived flag through the lifecycle orchestrator (so the on-disk repo and audit-log entry stay in sync with the HTML surface). Visibility changes also go through the orchestrator and emit the usual audit entry.

Soft-delete a repo

DELETE /api/v1/repos/{owner}/{repo}

Required scope: repo:write. The repo is soft-deleted (matches the web UI's deletion semantics — there is a grace window during which a site admin can restore it). Returns 204 on success; 404 for cross-user attempts and for already-deleted repos.

Not yet shipped

The §2 batch covered core CRUD. The following routes are planned and will land in later batches:

Method Path Notes
POST /api/v1/repos/{owner}/{repo}/forks Paired with shithub-cli's fork flow.
POST /api/v1/repos/{owner}/{repo}/merge-upstream Fork sync.
PUT /api/v1/repos/{owner}/{repo}/topics Topic replace.
GET /api/v1/repos/{owner}/{repo}/readme README content by ref.
POST /api/v1/repos/{template_owner}/{template_repo}/generate Create from template.
POST /api/v1/repos/{owner}/{repo}/transfer Owner transfer ack.
View source
1 # Repositories
2
3 The repository core REST surface. Mirrors GitHub's shape where
4 the model matches; deviations are called out inline. List routes
5 emit the standard `Link:` pagination header (see [overview](overview.md)).
6
7 ## Repo shape
8
9 Every list and single-repo response renders the same envelope:
10
11 ```json
12 {
13 "id": 12,
14 "name": "demo",
15 "full_name": "alice/demo",
16 "owner_login": "alice",
17 "owner_type": "user",
18 "description": "first cut",
19 "visibility": "public",
20 "private": false,
21 "default_branch": "trunk",
22 "fork": false,
23 "archived": false,
24 "has_issues": true,
25 "has_pulls": true,
26 "star_count": 0,
27 "watcher_count": 0,
28 "fork_count": 0,
29 "created_at": "2026-05-12T04:00:00Z",
30 "updated_at": "2026-05-12T04:00:00Z"
31 }
32 ```
33
34 `owner_type` is `"user"` or `"org"`. `default_branch` is
35 shithub's own default name (`trunk`), not GitHub's `main` — read
36 this field; don't hardcode.
37
38 ## List the authenticated user's repos
39
40 ```
41 GET /api/v1/user/repos
42 ```
43
44 Required scope: `repo:read`. Returns every repo the user owns,
45 private included. Paginated; `?per_page=` (≤100) and `?page=`.
46
47 ## List a user's public repos
48
49 ```
50 GET /api/v1/users/{username}/repos
51 ```
52
53 Required scope: `repo:read`. Returns the named user's public
54 repos. When the authenticated viewer is the named user, this
55 endpoint returns the same set as `GET /api/v1/user/repos`
56 (private included) to match the gh-shape.
57
58 ## List an org's repos
59
60 ```
61 GET /api/v1/orgs/{org}/repos
62 ```
63
64 Required scope: `repo:read`. Org members see every repo
65 (visibility-aware). Non-members see only public repos.
66
67 ## Get a single repo
68
69 ```
70 GET /api/v1/repos/{owner}/{repo}
71 ```
72
73 Required scope: `repo:read`. Returns the repo envelope above.
74 `404` when the caller can't see the repo (existence-leak-safe
75 treatment for private repos belonging to someone else).
76
77 ## Create a personal repo
78
79 ```
80 POST /api/v1/user/repos
81 ```
82
83 Required scope: `repo:write`. Body fields (all optional except
84 `name`):
85
86 ```json
87 {
88 "name": "demo",
89 "description": "first cut",
90 "visibility": "public",
91 "auto_init": true,
92 "license_template": "mit",
93 "gitignore_template": "Go"
94 }
95 ```
96
97 | Field | Type | Notes |
98 |----------------------|--------|-------------------------------------------------------|
99 | `name` | string | Lowercased, `[a-z0-9._-]`, 1–99 chars. |
100 | `description` | string | ≤350 chars. |
101 | `visibility` | string | `"public"` or `"private"`. Defaults to `"private"`. |
102 | `private` | bool | gh-compatible alternative to `visibility`. |
103 | `auto_init` | bool | Seed an initial README commit. |
104 | `license_template` | string | License key (e.g. `mit`); requires `auto_init=true`. |
105 | `gitignore_template` | string | Gitignore preset name; requires `auto_init=true`. |
106
107 Returns `201` with the repo envelope.
108
109 ### Errors
110
111 | Status | When |
112 |-------:|-----------------------------------------------------------------------|
113 | 401 | PAT missing/invalid. |
114 | 403 | PAT lacks `repo:write` scope. |
115 | 409 | Name already taken for this owner. |
116 | 422 | Invalid name, description too long, unknown license/gitignore template, or actor lacks a verified primary email. |
117
118 ## Create an org repo
119
120 ```
121 POST /api/v1/orgs/{org}/repos
122 ```
123
124 Required scope: `repo:write`. Same body shape as the personal
125 variant. Org members can create only when the org has
126 `allow_member_repo_create` enabled; owners (and site admins)
127 bypass that gate. Non-members get `404` (existence-leak-safe).
128
129 ## Update repo settings
130
131 ```
132 PATCH /api/v1/repos/{owner}/{repo}
133 ```
134
135 Required scope: `repo:write`. Only the fields you send are
136 modified; everything else stays as it was.
137
138 ```json
139 {
140 "description": "new copy",
141 "has_issues": false,
142 "has_pulls": true,
143 "archived": true,
144 "visibility": "private"
145 }
146 ```
147
148 Setting `archived` flips the archived flag through the lifecycle
149 orchestrator (so the on-disk repo and audit-log entry stay in
150 sync with the HTML surface). Visibility changes also go through
151 the orchestrator and emit the usual audit entry.
152
153 ## Soft-delete a repo
154
155 ```
156 DELETE /api/v1/repos/{owner}/{repo}
157 ```
158
159 Required scope: `repo:write`. The repo is soft-deleted (matches
160 the web UI's deletion semantics — there is a grace window during
161 which a site admin can restore it). Returns `204` on success;
162 `404` for cross-user attempts and for already-deleted repos.
163
164 ## Not yet shipped
165
166 The §2 batch covered core CRUD. The following routes are
167 **planned** and will land in later batches:
168
169 | Method | Path | Notes |
170 |--------|-------------------------------------------------------|--------------------------------------|
171 | POST | `/api/v1/repos/{owner}/{repo}/forks` | Paired with shithub-cli's fork flow. |
172 | POST | `/api/v1/repos/{owner}/{repo}/merge-upstream` | Fork sync. |
173 | PUT | `/api/v1/repos/{owner}/{repo}/topics` | Topic replace. |
174 | GET | `/api/v1/repos/{owner}/{repo}/readme` | README content by ref. |
175 | POST | `/api/v1/repos/{template_owner}/{template_repo}/generate` | Create from template. |
176 | POST | `/api/v1/repos/{owner}/{repo}/transfer` | Owner transfer ack. |