Webhooks
The webhook delivery format (payloads, signing) and the
webhook management API (CRUD over webhooks on a repo) are
both shipped. The management endpoints are PAT-authenticated and
share the canonical API conventions (JSON error
envelopes, X-RateLimit-*, X-OAuth-Scopes, Link
pagination).
Delivery format
See Webhooks (user docs) for the full delivery contract — headers, body framing, signature verification, retries, idempotency.
The user-docs page is intentionally the canonical place; an API consumer building a subscriber endpoint reads the same material.
Management API
All endpoints require an Authorization: Bearer <pat> header
whose token carries the repo:write scope and the caller's role
on the repo must reach settings:general (owner / write
collaborator). Webhook secrets are write-only: they're set on
create and rotated by passing a new secret on PATCH, but
never returned in any response.
| Method | Path |
|---|---|
| GET | /api/v1/repos/{owner}/{repo}/hooks |
| POST | /api/v1/repos/{owner}/{repo}/hooks |
| GET | /api/v1/repos/{owner}/{repo}/hooks/{id} |
| PATCH | /api/v1/repos/{owner}/{repo}/hooks/{id} |
| DELETE | /api/v1/repos/{owner}/{repo}/hooks/{id} |
| GET | /api/v1/repos/{owner}/{repo}/hooks/{id}/deliveries |
| GET | /api/v1/repos/{owner}/{repo}/hooks/{id}/deliveries/{did} |
| POST | /api/v1/repos/{owner}/{repo}/hooks/{id}/deliveries/{did}/redeliver |
Create
POST /api/v1/repos/alice/demo/hooks
Authorization: Bearer <pat>
Content-Type: application/json
{
"url": "https://hooks.example.com/sink",
"content_type": "json",
"events": ["push", "pull_request"],
"active": true,
"ssl_verification": true,
"secret": "shared-secret-or-omit-to-mint"
}
content_type is json (default) or form. Omitting secret
mints a fresh one server-side. The server validates the URL
against the SSRF allow-list (scheme + port + non-private
resolution) so a 422 here means the target was rejected at
create time — no silent delivery failures later.
A successful create returns the hook row at 201 Created and
enqueues a synthetic ping delivery so the operator sees an
immediate round-trip.
Patch
PATCH /api/v1/repos/alice/demo/hooks/42
Content-Type: application/json
{
"url": "https://hooks.example.com/v2",
"events": ["push", "pull_request", "issues"],
"active": false,
"secret": "rotated-secret"
}
Fields are merged onto the current row: omit a field to keep its
existing value. Passing secret rotates the HMAC key used for
subsequent deliveries.
Deliveries
The deliveries list is paginated (default 30, max 100 per page)
and emits standard Link: <...>; rel="next" … headers. The
single-delivery shape includes the captured request headers,
request body, and response body so operators can replay a
failed delivery from the recorded transcript.
POST .../deliveries/{did}/redeliver enqueues a fresh delivery
copying the same payload + headers under a new id; the response
body is {"id": <new_delivery_id>}.
Event types (canonical list)
The events shippable today, by X-Shithub-Event header:
pushpull_request(actions:opened,closed,merged,reopened,edited,ready_for_review,converted_to_draft,synchronize)pull_request_review(actions:submitted,dismissed)pull_request_review_commentissues(actions:opened,closed,reopened,edited,assigned,unassigned,labeled,unlabeled)issue_commentcheck_run(actions:created,completed,rerequested)check_suite(actions:requested,completed,rerequested)workflow_run(actions:queued,running,completed)workflow_job(actions:queued,running,completed,cancelled)starforkrepository(actions:created,deleted,archived,unarchived,renamed,transferred,publicized,privatized)ping(test event you trigger manually)
Each event's payload is documented per-type in the webhook detail page's "Recent deliveries" inspector — that's currently the authoritative reference until per-event documentation lands here.
Actions payload safety
workflow_run and workflow_job payloads are structural snapshots:
ids, run index, workflow path/name, head SHA/ref, event kind, status,
conclusion, timestamps, job key/name, runner id, needs, timeout, and
cancellation state. They intentionally do not include workflow
event payloads, env, permissions, logs, runner JWTs, or secret values.
View source
| 1 | # Webhooks |
| 2 | |
| 3 | The webhook **delivery format** (payloads, signing) and the |
| 4 | webhook **management API** (CRUD over webhooks on a repo) are |
| 5 | both shipped. The management endpoints are PAT-authenticated and |
| 6 | share the canonical [API conventions](overview.md) (JSON error |
| 7 | envelopes, `X-RateLimit-*`, `X-OAuth-Scopes`, `Link` |
| 8 | pagination). |
| 9 | |
| 10 | ## Delivery format |
| 11 | |
| 12 | See [Webhooks (user docs)](../user/webhooks.md) for the full |
| 13 | delivery contract — headers, body framing, signature verification, |
| 14 | retries, idempotency. |
| 15 | |
| 16 | The user-docs page is intentionally the canonical place; an API |
| 17 | consumer building a subscriber endpoint reads the same material. |
| 18 | |
| 19 | ## Management API |
| 20 | |
| 21 | All endpoints require an `Authorization: Bearer <pat>` header |
| 22 | whose token carries the `repo:write` scope and the caller's role |
| 23 | on the repo must reach **settings:general** (owner / write |
| 24 | collaborator). Webhook secrets are write-only: they're set on |
| 25 | create and rotated by passing a new `secret` on PATCH, but |
| 26 | **never** returned in any response. |
| 27 | |
| 28 | | Method | Path | |
| 29 | |--------|----------------------------------------------------------------------------| |
| 30 | | GET | `/api/v1/repos/{owner}/{repo}/hooks` | |
| 31 | | POST | `/api/v1/repos/{owner}/{repo}/hooks` | |
| 32 | | GET | `/api/v1/repos/{owner}/{repo}/hooks/{id}` | |
| 33 | | PATCH | `/api/v1/repos/{owner}/{repo}/hooks/{id}` | |
| 34 | | DELETE | `/api/v1/repos/{owner}/{repo}/hooks/{id}` | |
| 35 | | GET | `/api/v1/repos/{owner}/{repo}/hooks/{id}/deliveries` | |
| 36 | | GET | `/api/v1/repos/{owner}/{repo}/hooks/{id}/deliveries/{did}` | |
| 37 | | POST | `/api/v1/repos/{owner}/{repo}/hooks/{id}/deliveries/{did}/redeliver` | |
| 38 | |
| 39 | ### Create |
| 40 | |
| 41 | ```http |
| 42 | POST /api/v1/repos/alice/demo/hooks |
| 43 | Authorization: Bearer <pat> |
| 44 | Content-Type: application/json |
| 45 | |
| 46 | { |
| 47 | "url": "https://hooks.example.com/sink", |
| 48 | "content_type": "json", |
| 49 | "events": ["push", "pull_request"], |
| 50 | "active": true, |
| 51 | "ssl_verification": true, |
| 52 | "secret": "shared-secret-or-omit-to-mint" |
| 53 | } |
| 54 | ``` |
| 55 | |
| 56 | `content_type` is `json` (default) or `form`. Omitting `secret` |
| 57 | mints a fresh one server-side. The server validates the URL |
| 58 | against the SSRF allow-list (scheme + port + non-private |
| 59 | resolution) so a 422 here means the target was rejected at |
| 60 | create time — no silent delivery failures later. |
| 61 | |
| 62 | A successful create returns the hook row at `201 Created` and |
| 63 | enqueues a synthetic `ping` delivery so the operator sees an |
| 64 | immediate round-trip. |
| 65 | |
| 66 | ### Patch |
| 67 | |
| 68 | ```http |
| 69 | PATCH /api/v1/repos/alice/demo/hooks/42 |
| 70 | Content-Type: application/json |
| 71 | |
| 72 | { |
| 73 | "url": "https://hooks.example.com/v2", |
| 74 | "events": ["push", "pull_request", "issues"], |
| 75 | "active": false, |
| 76 | "secret": "rotated-secret" |
| 77 | } |
| 78 | ``` |
| 79 | |
| 80 | Fields are merged onto the current row: omit a field to keep its |
| 81 | existing value. Passing `secret` rotates the HMAC key used for |
| 82 | subsequent deliveries. |
| 83 | |
| 84 | ### Deliveries |
| 85 | |
| 86 | The deliveries list is paginated (default 30, max 100 per page) |
| 87 | and emits standard `Link: <...>; rel="next" …` headers. The |
| 88 | single-delivery shape includes the captured request headers, |
| 89 | request body, and response body so operators can replay a |
| 90 | failed delivery from the recorded transcript. |
| 91 | |
| 92 | `POST .../deliveries/{did}/redeliver` enqueues a fresh delivery |
| 93 | copying the same payload + headers under a new id; the response |
| 94 | body is `{"id": <new_delivery_id>}`. |
| 95 | |
| 96 | ## Event types (canonical list) |
| 97 | |
| 98 | The events shippable today, by `X-Shithub-Event` header: |
| 99 | |
| 100 | - `push` |
| 101 | - `pull_request` (actions: `opened`, `closed`, `merged`, |
| 102 | `reopened`, `edited`, `ready_for_review`, `converted_to_draft`, |
| 103 | `synchronize`) |
| 104 | - `pull_request_review` (actions: `submitted`, `dismissed`) |
| 105 | - `pull_request_review_comment` |
| 106 | - `issues` (actions: `opened`, `closed`, `reopened`, `edited`, |
| 107 | `assigned`, `unassigned`, `labeled`, `unlabeled`) |
| 108 | - `issue_comment` |
| 109 | - `check_run` (actions: `created`, `completed`, `rerequested`) |
| 110 | - `check_suite` (actions: `requested`, `completed`, |
| 111 | `rerequested`) |
| 112 | - `workflow_run` (actions: `queued`, `running`, `completed`) |
| 113 | - `workflow_job` (actions: `queued`, `running`, `completed`, |
| 114 | `cancelled`) |
| 115 | - `star` |
| 116 | - `fork` |
| 117 | - `repository` (actions: `created`, `deleted`, `archived`, |
| 118 | `unarchived`, `renamed`, `transferred`, `publicized`, |
| 119 | `privatized`) |
| 120 | - `ping` (test event you trigger manually) |
| 121 | |
| 122 | Each event's payload is documented per-type in the webhook detail |
| 123 | page's "Recent deliveries" inspector — that's currently the |
| 124 | authoritative reference until per-event documentation lands here. |
| 125 | |
| 126 | ### Actions payload safety |
| 127 | |
| 128 | `workflow_run` and `workflow_job` payloads are structural snapshots: |
| 129 | ids, run index, workflow path/name, head SHA/ref, event kind, status, |
| 130 | conclusion, timestamps, job key/name, runner id, needs, timeout, and |
| 131 | cancellation state. They intentionally do **not** include workflow |
| 132 | event payloads, env, permissions, logs, runner JWTs, or secret values. |