tenseleyflow/shithub / 1938837

Browse files

S38: docs — API reference (overview/auth/users/repos/issues/pulls/checks/webhooks/search/admin)

Authored by espadonne
SHA
1938837a704f323e466846e4b4be0ce3106d6bda
Parents
2f1f48e
Tree
1b4bed0

10 changed files

StatusFile+-
A docs/public/api/admin.md 38 0
A docs/public/api/auth.md 55 0
A docs/public/api/checks.md 151 0
A docs/public/api/issues.md 27 0
A docs/public/api/overview.md 83 0
A docs/public/api/pulls.md 23 0
A docs/public/api/repos.md 31 0
A docs/public/api/search.md 43 0
A docs/public/api/users.md 89 0
A docs/public/api/webhooks.md 57 0
docs/public/api/admin.mdadded
@@ -0,0 +1,38 @@
1
+# Admin (site-admin only)
2
+
3
+> **Planned.** The admin API is not exposed yet. Site-admin
4
+> actions today are reachable through the `/admin/` web UI and
5
+> the `shithubd admin` CLI subcommands.
6
+
7
+The site-admin surface is intentionally narrow: most operator
8
+actions go through the CLI (`shithubd admin …`) where they're
9
+auditable from journal logs. The planned API here exists for
10
+automation that's already authenticated as a site admin (e.g.,
11
+an SSO/SCIM bridge).
12
+
13
+## Planned routes
14
+
15
+| Method | Path                                     | Scope          | Purpose                          |
16
+|--------|------------------------------------------|----------------|----------------------------------|
17
+| GET    | `/api/v1/admin/users`                    | site-admin     | List users (paginated).          |
18
+| GET    | `/api/v1/admin/users/{id}`               | site-admin     | One user, with admin-only fields.|
19
+| POST   | `/api/v1/admin/users/{id}/suspend`       | site-admin     | Freeze the account.              |
20
+| POST   | `/api/v1/admin/users/{id}/reinstate`     | site-admin     | Un-freeze.                       |
21
+| POST   | `/api/v1/admin/users/{id}/reset-password`| site-admin     | Force a password reset email.    |
22
+| POST   | `/api/v1/admin/users/{id}/site-admin`    | site-admin     | Grant or revoke site-admin bit.  |
23
+
24
+## Authorization
25
+
26
+A regular PAT — even one held by a user who is a site admin — is
27
+**not enough** by itself; the admin endpoints require both:
28
+
29
+- The token's owner has the `is_site_admin` flag set.
30
+- The token has the `admin:site` scope (separate from `admin:org`).
31
+
32
+Both checks must pass; either alone returns 403.
33
+
34
+## Audit
35
+
36
+Every admin API call writes to the same `admin_audit_log` the
37
+web admin UI uses. Each row carries the calling site admin's id,
38
+the target id, the action, and the request IP.
docs/public/api/auth.mdadded
@@ -0,0 +1,55 @@
1
+# Authentication
2
+
3
+shithub's API is PAT-only. There is no OAuth / device-flow / JWT
4
+issuance endpoint today.
5
+
6
+## Header
7
+
8
+```
9
+Authorization: Bearer shp_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
10
+```
11
+
12
+`Authorization: token shp_…` is accepted as a synonym for tools
13
+that hard-code GitHub's older syntax.
14
+
15
+## Token format
16
+
17
+PATs are 40 characters of base32 with the `shp_` prefix. They
18
+match the regex:
19
+
20
+```
21
+shp_[A-Za-z0-9]{40}
22
+```
23
+
24
+Secret-scanning tools (GitHub's, GitGuardian's, etc.) recognize
25
+this prefix.
26
+
27
+## Failure modes
28
+
29
+| Status | Body                              | Cause                                                |
30
+|-------:|-----------------------------------|------------------------------------------------------|
31
+|    401 | `{"error":"unauthenticated"}`     | Missing or malformed header.                          |
32
+|    401 | `{"error":"invalid token"}`       | Token doesn't exist, was revoked, or has expired.    |
33
+|    401 | `{"error":"account suspended"}`   | The owning account has been suspended by an admin.   |
34
+|    403 | `{"error":"insufficient scope"}`  | Token is valid but lacks the scope this route needs. |
35
+
36
+## Sessions
37
+
38
+The web UI uses session cookies, not PATs. Session cookies are
39
+**not accepted** on `/api/v1/` — the API is PAT-only. This is a
40
+deliberate choice: it keeps CSRF concerns off the API surface
41
+and means every API caller is identified by an auditable token.
42
+
43
+## Creating a token programmatically
44
+
45
+There is no API for creating PATs; tokens are only created from
46
+the web UI. This is intentional — the create-PAT surface is the
47
+account's most security-sensitive non-password operation.
48
+
49
+## Future
50
+
51
+OAuth-style application authorizations (`client_id` + `client_
52
+secret`, `code` exchange, refresh tokens) are planned post-MVP.
53
+For now, instruct human users to mint a PAT from their settings
54
+and supply it to your app via the operator's secret-management
55
+flow.
docs/public/api/checks.mdadded
@@ -0,0 +1,151 @@
1
+# Status checks
2
+
3
+External CI systems publish status into shithub via the check-runs
4
+API. Branch-protection rules can require named contexts to pass
5
+before a PR merges.
6
+
7
+## Concepts
8
+
9
+- A **check run** is one invocation: `(repo, head_sha, name)`
10
+  with a state and metadata.
11
+- A **check suite** is the bag of check runs for one head SHA;
12
+  shithub computes it server-side.
13
+- Branch protection's "required status checks" matches by **name**
14
+  on the head commit.
15
+
16
+## Create a check run
17
+
18
+```
19
+POST /api/v1/repos/{owner}/{repo}/check-runs
20
+```
21
+
22
+Required scope: `repo` (write).
23
+
24
+### Request body
25
+
26
+```json
27
+{
28
+  "name":       "ci/lint",
29
+  "head_sha":   "8c4e3f2a1b…",
30
+  "status":     "in_progress",
31
+  "started_at": "2026-05-09T16:00:00Z",
32
+  "details_url": "https://ci.example/runs/12345",
33
+  "external_id": "12345",
34
+  "output": {
35
+    "title":   "Lint",
36
+    "summary": "Running golangci-lint",
37
+    "text":    "..."
38
+  }
39
+}
40
+```
41
+
42
+| Field          | Required | Notes                                                      |
43
+|----------------|----------|------------------------------------------------------------|
44
+| `name`         | yes      | The context name. Match this on branch-protection rules.   |
45
+| `head_sha`     | yes      | 40-char SHA-1 of the commit being checked.                 |
46
+| `status`       | no       | `queued`, `in_progress`, `completed`. Default `queued`.    |
47
+| `conclusion`   | iff `status=completed` | `success`, `failure`, `neutral`, `cancelled`, `timed_out`, `action_required`. |
48
+| `started_at`   | no       | RFC 3339.                                                  |
49
+| `completed_at` | iff `status=completed` | RFC 3339.                                       |
50
+| `details_url`  | no       | Where humans go to inspect the run.                        |
51
+| `external_id`  | no       | Your system's identifier; echoed back on read.             |
52
+| `output`       | no       | `{title, summary, text}`. Summary is markdown.             |
53
+
54
+### Response
55
+
56
+`201 Created` with the full check-run resource:
57
+
58
+```json
59
+{
60
+  "id":         9876,
61
+  "name":       "ci/lint",
62
+  "head_sha":   "8c4e3f2a1b…",
63
+  "status":     "in_progress",
64
+  "conclusion": null,
65
+  "started_at": "2026-05-09T16:00:00Z",
66
+  "details_url": "https://ci.example/runs/12345",
67
+  "external_id": "12345",
68
+  "output":     { "title": "Lint", "summary": "Running golangci-lint" }
69
+}
70
+```
71
+
72
+## Update a check run
73
+
74
+```
75
+PATCH /api/v1/repos/{owner}/{repo}/check-runs/{id}
76
+```
77
+
78
+Required scope: `repo` (write).
79
+
80
+Same body shape as create; partial. Typical use: flip
81
+`status: "completed"` with a `conclusion`.
82
+
83
+### Conclusion semantics
84
+
85
+| Conclusion        | Effect on PR merge gate                                |
86
+|-------------------|--------------------------------------------------------|
87
+| `success`         | Counts as passing.                                     |
88
+| `failure`         | Blocks merge.                                          |
89
+| `neutral`         | Doesn't block; doesn't count as passing either.        |
90
+| `cancelled`       | Blocks merge.                                          |
91
+| `timed_out`       | Blocks merge.                                          |
92
+| `action_required` | Blocks merge with a human-action signal.               |
93
+
94
+## List check runs for a commit
95
+
96
+```
97
+GET /api/v1/repos/{owner}/{repo}/commits/{sha}/check-runs
98
+```
99
+
100
+Required scope: `repo:read`.
101
+
102
+Returns the most recent check run **per name** on this commit.
103
+Older runs are still in the database (audit) but not in this
104
+listing.
105
+
106
+```json
107
+{
108
+  "total_count": 2,
109
+  "check_runs": [
110
+    {"id": 9876, "name": "ci/lint",  "status": "completed", "conclusion": "success", ...},
111
+    {"id": 9877, "name": "ci/test",  "status": "in_progress", ...}
112
+  ]
113
+}
114
+```
115
+
116
+## List check suites for a commit
117
+
118
+```
119
+GET /api/v1/repos/{owner}/{repo}/commits/{sha}/check-suites
120
+```
121
+
122
+Required scope: `repo:read`.
123
+
124
+Returns the rolled-up suite view per `head_sha`:
125
+
126
+```json
127
+{
128
+  "total_count": 1,
129
+  "check_suites": [
130
+    {
131
+      "head_sha":  "8c4e3f2a1b…",
132
+      "status":    "in_progress",
133
+      "conclusion": null,
134
+      "checks_url": "/api/v1/repos/owner/repo/commits/8c4e3f2a1b…/check-runs"
135
+    }
136
+  ]
137
+}
138
+```
139
+
140
+## Idempotency
141
+
142
+Check-run creates with the same `(repo, head_sha, name,
143
+external_id)` are coalesced — re-publishing the same run from a
144
+retried CI job is safe.
145
+
146
+## Permissions
147
+
148
+The PAT must belong to a user with **write access** to the repo.
149
+Bot accounts representing CI runners are the typical pattern;
150
+they're regular shithub accounts with PATs scoped to `repo` and
151
+nothing else.
docs/public/api/issues.mdadded
@@ -0,0 +1,27 @@
1
+# Issues
2
+
3
+> **Planned.** Issues over the API are not yet shipped. The web
4
+> UI is the only authoring surface today.
5
+
6
+## Planned routes
7
+
8
+| Method | Path                                                   | Scope        |
9
+|--------|--------------------------------------------------------|--------------|
10
+| GET    | `/api/v1/repos/{owner}/{repo}/issues`                  | `repo:read`  |
11
+| GET    | `/api/v1/repos/{owner}/{repo}/issues/{number}`         | `repo:read`  |
12
+| POST   | `/api/v1/repos/{owner}/{repo}/issues`                  | `repo`       |
13
+| PATCH  | `/api/v1/repos/{owner}/{repo}/issues/{number}`         | `repo`       |
14
+| GET    | `/api/v1/repos/{owner}/{repo}/issues/{number}/comments`| `repo:read`  |
15
+| POST   | `/api/v1/repos/{owner}/{repo}/issues/{number}/comments`| `repo`       |
16
+| PATCH  | `/api/v1/repos/{owner}/{repo}/issues/comments/{id}`    | `repo`       |
17
+| DELETE | `/api/v1/repos/{owner}/{repo}/issues/comments/{id}`    | `repo`       |
18
+
19
+Filters on the list endpoint will mirror the web filters
20
+(`state`, `author`, `assignee`, `label`, `milestone`, `since`,
21
+`sort`, `direction`).
22
+
23
+## Markdown rendering
24
+
25
+Posted bodies are stored as raw markdown. Rendering happens at
26
+read time, with the same `internal/markdown` pipeline the web UI
27
+uses, so an API consumer sees the same HTML the browser would.
docs/public/api/overview.mdadded
@@ -0,0 +1,83 @@
1
+# API overview
2
+
3
+shithub exposes a small REST API at `/api/v1/`. It's
4
+PAT-authenticated, JSON-bodied, and CSRF-exempt.
5
+
6
+> **Status.** The API is intentionally narrow today. Endpoints
7
+> currently shipped: `GET /api/v1/user`, the
8
+> `/api/v1/repos/{owner}/{repo}/check-runs` family, and the
9
+> `/api/v1/user/starred*` stars endpoints. Other sections of this
10
+> reference (Issues, Pull requests, Webhooks, etc.) describe the
11
+> **planned** shape and will land in subsequent sprints. Pages
12
+> that document planned-only endpoints carry a banner.
13
+
14
+## Authentication
15
+
16
+Every API request requires a personal access token. See
17
+[Personal access tokens](../user/personal-access-tokens.md) for
18
+how to create one.
19
+
20
+```
21
+Authorization: Bearer shp_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
22
+```
23
+
24
+`Authorization: token shp_…` is also accepted as a synonym.
25
+
26
+A request with no `Authorization` header — or with an invalid /
27
+expired / revoked PAT — returns `401 Unauthorized` with:
28
+
29
+```json
30
+{"error": "unauthenticated"}
31
+```
32
+
33
+A request whose PAT lacks the scope a route requires returns
34
+`403 Forbidden` with:
35
+
36
+```json
37
+{"error": "insufficient scope"}
38
+```
39
+
40
+## Scopes
41
+
42
+Every route declares the scope(s) a token must hold. See
43
+[scopes table](../user/personal-access-tokens.md#scopes).
44
+
45
+Scopes are **grants only** — a token cannot do something the
46
+underlying user cannot. Holding `repo` does not let a token push
47
+to a repo the user has no access to.
48
+
49
+## Conventions
50
+
51
+- **Base URL:** `https://<your-instance>/api/v1/`
52
+- **Content type:** `application/json; charset=utf-8` for
53
+  request bodies and responses.
54
+- **Error responses:** `{"error": "<short message>"}` with a
55
+  conventional HTTP status.
56
+- **Cache-Control:** every API response sets `no-store`.
57
+- **Pagination:** list endpoints accept `?per_page=` (default 30,
58
+  max 100) and return a `Link:` header with `next`, `prev`,
59
+  `first`, `last` URLs (RFC 5988). Cursor pagination on hot lists
60
+  uses `?cursor=…` and returns the next cursor in the `Link:`
61
+  header.
62
+- **Body cap:** request bodies are capped at 256 KiB. Larger
63
+  payloads return `413`.
64
+- **Rate limits:** every response includes `X-RateLimit-Limit`,
65
+  `X-RateLimit-Remaining`, and `X-RateLimit-Reset` headers (Unix
66
+  timestamp). Exceeding the limit returns `429`.
67
+
68
+## Versioning
69
+
70
+The path prefix `/api/v1/` is the version. Backwards-incompatible
71
+changes will land under `/api/v2/`. Additions (new endpoints, new
72
+fields on responses) are not breaking and land under v1.
73
+
74
+## Date format
75
+
76
+All timestamps are RFC 3339 UTC: `2026-05-09T16:30:00Z`.
77
+
78
+## ID stability
79
+
80
+Numeric IDs are stable for the life of the row; reusing a name
81
+slot doesn't reuse the ID. URLs that take a `{owner}` and `{repo}`
82
+will redirect after a rename — but external callers should
83
+prefer numeric IDs where the API exposes them.
docs/public/api/pulls.mdadded
@@ -0,0 +1,23 @@
1
+# Pull requests
2
+
3
+> **Planned.** Pull request endpoints are not yet shipped.
4
+
5
+## Planned routes
6
+
7
+| Method | Path                                                     | Scope        |
8
+|--------|----------------------------------------------------------|--------------|
9
+| GET    | `/api/v1/repos/{owner}/{repo}/pulls`                     | `repo:read`  |
10
+| GET    | `/api/v1/repos/{owner}/{repo}/pulls/{number}`            | `repo:read`  |
11
+| POST   | `/api/v1/repos/{owner}/{repo}/pulls`                     | `repo`       |
12
+| PATCH  | `/api/v1/repos/{owner}/{repo}/pulls/{number}`            | `repo`       |
13
+| GET    | `/api/v1/repos/{owner}/{repo}/pulls/{number}/files`      | `repo:read`  |
14
+| GET    | `/api/v1/repos/{owner}/{repo}/pulls/{number}/commits`    | `repo:read`  |
15
+| GET    | `/api/v1/repos/{owner}/{repo}/pulls/{number}/reviews`    | `repo:read`  |
16
+| POST   | `/api/v1/repos/{owner}/{repo}/pulls/{number}/reviews`    | `repo`       |
17
+| PUT    | `/api/v1/repos/{owner}/{repo}/pulls/{number}/merge`      | `repo`       |
18
+| GET    | `/api/v1/repos/{owner}/{repo}/pulls/{number}/comments`   | `repo:read`  |
19
+| POST   | `/api/v1/repos/{owner}/{repo}/pulls/{number}/comments`   | `repo`       |
20
+
21
+The merge endpoint is gated by branch protection: status checks,
22
+required reviewers, and conversation-resolution rules apply
23
+identically to API-driven and UI-driven merges.
docs/public/api/repos.mdadded
@@ -0,0 +1,31 @@
1
+# Repositories
2
+
3
+> **Planned.** The repository CRUD API surface is not yet
4
+> shipped. Today the repo lifecycle is driven through the web UI
5
+> and the `shithubd` CLI. The shape below is the **planned** v1
6
+> surface; it will land in a follow-up sprint.
7
+
8
+## Planned routes
9
+
10
+| Method | Path                                        | Scope        | Purpose                       |
11
+|--------|---------------------------------------------|--------------|-------------------------------|
12
+| GET    | `/api/v1/repos/{owner}/{repo}`              | `repo:read`  | Get one repo.                 |
13
+| POST   | `/api/v1/user/repos`                        | `repo`       | Create a repo under the user. |
14
+| POST   | `/api/v1/orgs/{org}/repos`                  | `repo,admin:org` | Create a repo under an org. |
15
+| PATCH  | `/api/v1/repos/{owner}/{repo}`              | `repo`       | Edit settings.                |
16
+| DELETE | `/api/v1/repos/{owner}/{repo}`              | `repo`       | Delete (with confirmation).   |
17
+| GET    | `/api/v1/repos/{owner}/{repo}/branches`     | `repo:read`  | List branches.                |
18
+| GET    | `/api/v1/repos/{owner}/{repo}/branches/{branch}/protection` | `repo:read` | Get branch protection rules. |
19
+| PUT    | `/api/v1/repos/{owner}/{repo}/branches/{branch}/protection` | `repo`     | Replace branch protection.   |
20
+
21
+The web UI's behavior is the de-facto specification — when these
22
+endpoints land, they implement the same checks (visibility, name
23
+collisions, owner-permissions, soft-delete grace) that the web
24
+forms enforce.
25
+
26
+## Today
27
+
28
+Until the API ships, scripted repo lifecycle is best done via
29
+the `shithubd` operator CLI on the host (operator-only) or
30
+through HTML form submission with a session cookie (per-user but
31
+not API-shaped).
docs/public/api/search.mdadded
@@ -0,0 +1,43 @@
1
+# Search
2
+
3
+> **Planned.** Search over the API is not shipped yet. The web UI's
4
+> `/search` is the only entry point today.
5
+
6
+## Planned routes
7
+
8
+| Method | Path                              | Scope                          |
9
+|--------|-----------------------------------|--------------------------------|
10
+| GET    | `/api/v1/search/code`             | None (public corpus only without `repo:read`); `repo:read` to include private. |
11
+| GET    | `/api/v1/search/repositories`     | Same scoping.                  |
12
+| GET    | `/api/v1/search/issues`           | Same scoping.                  |
13
+| GET    | `/api/v1/search/users`            | None.                          |
14
+
15
+Query parameters:
16
+
17
+- `q=` — search query, with the same operators as
18
+  [Search (user docs)](../user/search.md).
19
+- `sort=` — `created`, `updated`, `stars`, `relevance` (default).
20
+- `order=` — `asc`, `desc`.
21
+- `per_page=`, `cursor=`.
22
+
23
+Response shape (proposed):
24
+
25
+```json
26
+{
27
+  "total_count": 142,
28
+  "items": [
29
+    { /* type-dependent record */ }
30
+  ]
31
+}
32
+```
33
+
34
+The total is **counted to a cap** (10000) — for larger result
35
+sets the API returns "10000+" semantics rather than counting
36
+exactly. Consumers that need totals should narrow the query.
37
+
38
+## Visibility
39
+
40
+Search results are filtered by the requesting PAT's user's
41
+visibility, identical to the web UI's behavior. A token cannot
42
+see a result it would not see in a browser logged in as the
43
+same user.
docs/public/api/users.mdadded
@@ -0,0 +1,89 @@
1
+# Users
2
+
3
+## Get the authenticated user
4
+
5
+```
6
+GET /api/v1/user
7
+```
8
+
9
+Required scope: `user:read`.
10
+
11
+Returns the user record for the account that owns the
12
+authenticating PAT.
13
+
14
+### Response
15
+
16
+```json
17
+{
18
+  "id": 42,
19
+  "username": "alice",
20
+  "name": "Alice Example",
21
+  "email_verified": true,
22
+  "created_at": "2026-05-09T16:30:00Z"
23
+}
24
+```
25
+
26
+| Field            | Type    | Notes                                                |
27
+|------------------|---------|------------------------------------------------------|
28
+| `id`             | int64   | Stable numeric ID.                                   |
29
+| `username`       | string  | Account username; URL-safe slug.                     |
30
+| `name`           | string  | Display name; may be empty if not set.               |
31
+| `email_verified` | bool    | Whether the primary email has been verified.         |
32
+| `created_at`     | string  | RFC 3339 UTC timestamp of account creation.          |
33
+
34
+### Errors
35
+
36
+| Status | When                                |
37
+|-------:|-------------------------------------|
38
+|    401 | PAT missing/invalid/expired/revoked. |
39
+|    403 | PAT lacks `user:read` scope.         |
40
+|    404 | User record not found (suspended or deleted between auth and lookup). |
41
+
42
+## Get a user by username
43
+
44
+> **Planned.** `GET /api/v1/users/{username}` is not shipped yet.
45
+
46
+## Update the authenticated user
47
+
48
+> **Planned.** `PATCH /api/v1/user` is not shipped yet.
49
+
50
+## List the authenticated user's repos
51
+
52
+> **Planned.** `GET /api/v1/user/repos` is not shipped yet.
53
+
54
+## Stars
55
+
56
+The starred-repos surface for the authenticating user.
57
+
58
+### List starred repos
59
+
60
+```
61
+GET /api/v1/user/starred
62
+```
63
+
64
+Required scope: `user:read`.
65
+
66
+Returns the list of `(owner, repo)` pairs the user has starred,
67
+most-recent first. Pagination via `?cursor=…` and `?per_page=`.
68
+
69
+### Star a repo
70
+
71
+```
72
+PUT /api/v1/user/starred/{owner}/{repo}
73
+```
74
+
75
+Required scope: `user` (write).
76
+
77
+Idempotent: starring an already-starred repo returns `204` and
78
+does not duplicate the row. Returns `404` if the repo doesn't
79
+exist or the user can't see it.
80
+
81
+### Unstar a repo
82
+
83
+```
84
+DELETE /api/v1/user/starred/{owner}/{repo}
85
+```
86
+
87
+Required scope: `user` (write).
88
+
89
+Idempotent: unstarring a not-starred repo returns `204`.
docs/public/api/webhooks.mdadded
@@ -0,0 +1,57 @@
1
+# Webhooks
2
+
3
+The webhook **delivery format** (payloads, signing) is shipped
4
+and stable. The webhook **management API** (CRUD over webhooks
5
+on a repo) is planned.
6
+
7
+## Delivery format
8
+
9
+See [Webhooks (user docs)](../user/webhooks.md) for the full
10
+delivery contract — headers, body framing, signature verification,
11
+retries, idempotency.
12
+
13
+The user-docs page is intentionally the canonical place; an API
14
+consumer building a subscriber endpoint reads the same material.
15
+
16
+## Management API (planned)
17
+
18
+| Method | Path                                                      | Scope        |
19
+|--------|-----------------------------------------------------------|--------------|
20
+| GET    | `/api/v1/repos/{owner}/{repo}/hooks`                      | `webhooks`   |
21
+| POST   | `/api/v1/repos/{owner}/{repo}/hooks`                      | `webhooks`   |
22
+| GET    | `/api/v1/repos/{owner}/{repo}/hooks/{id}`                 | `webhooks`   |
23
+| PATCH  | `/api/v1/repos/{owner}/{repo}/hooks/{id}`                 | `webhooks`   |
24
+| DELETE | `/api/v1/repos/{owner}/{repo}/hooks/{id}`                 | `webhooks`   |
25
+| POST   | `/api/v1/repos/{owner}/{repo}/hooks/{id}/pings`           | `webhooks`   |
26
+| GET    | `/api/v1/repos/{owner}/{repo}/hooks/{id}/deliveries`      | `webhooks`   |
27
+| POST   | `/api/v1/repos/{owner}/{repo}/hooks/{id}/deliveries/{delivery}/redeliver` | `webhooks` |
28
+
29
+Until these land, manage webhooks via the web UI under
30
+Repository → Settings → Webhooks.
31
+
32
+## Event types (canonical list)
33
+
34
+The events shippable today, by `X-Shithub-Event` header:
35
+
36
+- `push`
37
+- `pull_request` (actions: `opened`, `closed`, `merged`,
38
+  `reopened`, `edited`, `ready_for_review`, `converted_to_draft`,
39
+  `synchronize`)
40
+- `pull_request_review` (actions: `submitted`, `dismissed`)
41
+- `pull_request_review_comment`
42
+- `issues` (actions: `opened`, `closed`, `reopened`, `edited`,
43
+  `assigned`, `unassigned`, `labeled`, `unlabeled`)
44
+- `issue_comment`
45
+- `check_run` (actions: `created`, `completed`, `rerequested`)
46
+- `check_suite` (actions: `requested`, `completed`,
47
+  `rerequested`)
48
+- `star`
49
+- `fork`
50
+- `repository` (actions: `created`, `deleted`, `archived`,
51
+  `unarchived`, `renamed`, `transferred`, `publicized`,
52
+  `privatized`)
53
+- `ping` (test event you trigger manually)
54
+
55
+Each event's payload is documented per-type in the webhook detail
56
+page's "Recent deliveries" inspector — that's currently the
57
+authoritative reference until per-event documentation lands here.