Webhook payloads
On this page 3
Every event this instance can send, generated from the module the payloads are built from.
Generated from OpenAPI 1.0.0.
The envelope
Every payload has the same shape, so a receiver can be written once:
{
"event": "pr:opened",
"delivered_at": "2026-08-13T10:00:00.000Z",
"repository": { "full_name": "owner/name", "owner": "owner", "name": "name", "id": 1 },
"sender": { "handle": "somebody", "id": 2 },
"subject": { "type": "pull_request", "id": 3, "number": 12, "title": "A change", "url": "/owner/name/pull/12" },
"action": "opened"
}
Three rules the shapes follow:
- Numbers are numbers and times are ISO 8601 strings. Both survive JSON, both parse everywhere, and neither depends on knowing this instance's timezone.
senderis null when nothing did it. A scheduled action or a recovery sweep has no person behind it, and a receiver that assumes one crashes on the day it is missing.- Nothing is a database row. These are named fields chosen for the receiver, so a column rename here is not a breaking change to somebody's CI.
Signing
Each delivery carries a signature over the exact bytes sent. Verify it before trusting the body: a webhook endpoint that skips the check is an endpoint anybody can post to.
The events
| Event | Sent when |
|---|---|
pr:opened | somebody opened a pull request |
pr:synchronized | the head of an open pull request moved, so a review already written may be stale |
pr:ready_for_review | a draft became ready, and nothing else about it changed |
pr:merged | the work landed |
pr:closed | the work will not land, and anything queued against it should stop |
review:requested | somebody is waiting on a specific reviewer |
review:submitted | a verdict arrived |
issue:opened | there is something new to triage |
issue:closed | work queued against it should stop |
comment:created | somebody replied |
release:published | a release went out |
check:reported | a check run said something about a commit; action is its status |
run:transitioned | a workflow run changed state; action is the new one |
job:transitioned | one job of a run changed state; action is the new one |
run:action_required | a run has stopped and needs somebody; action is approval, gate or event |
artifact:expired | an artifact passed its date and was deleted; the file is already gone when this arrives |
deployment:status | a deployment was recorded, changed state, or was rolled back; action is the state |
status:reported | the older commit-status API said something; action is its state |
test:monitor | a rule about the tests changed state; action is alarm or recovered |
test:flaky | a test crossed from steady to unreliable, once rather than every run |
test:recorded | a suite reported its results, with the totals and the run id to read the detail from |
check:reported, status:reported, run:transitioned and job:transitioned carry an extra
key - check, run or job - and put the new state in action, so one subscription covers a
whole lifecycle and a receiver switches on one field.