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. | |