GitHub Webhook Example

Inspect a GitHub webhook request as it arrives at your endpoint. See the event JSON and the headers GitHub sends with it. Find out how a delivery is signed and what happens when one fails. Catch a real GitHub webhook on a URL of your own with the ReqBin Webhook Tester. No sign-up, nothing to install.

What is a GitHub webhook?

A GitHub webhook is an HTTP POST request that GitHub sends to a URL you registered, telling your server that something happened in a repository or an organization: an issue was opened, a branch was pushed, a pull request was merged, a release was published.

How do I receive GitHub webhooks while developing on localhost?

GitHub can deliver requests only to a URL that is reachable from the internet. A dev server on localhost is not, so nothing GitHub sends arrives there. To make your localhost URL public, you need to install a tunnel, which is not a trivial task, or run smee-client.

A simpler option is to use a ReqBin webhook URL. It is public, it takes whatever GitHub posts to it, and it shows you each request, as soon as it lands.

How do I connect a ReqBin webhook URL to GitHub?

  1. Create a webhook URL with the ReqBin Webhook Tester and copy it.
  2. In the repository, register that URL as a webhook:
    1. Open Settings and click Webhooks in the left sidebar.
    2. Click Add webhook and paste the ReqBin URL into Payload URL.
    3. Choose application/json as the Content type and select the events you want to receive.
    4. Click Add webhook. GitHub sends a ping delivery straight away.
  3. Open an issue or push a commit and read the GitHub webhook details on your inbox page.

Can I edit a caught request and send it to my own server?

Yes. On the inbox page, select any of the requests you received from GitHub and press Open in API Tester. The request opens in the ReqBin API tester with everything that arrived. Enter your URL, change what you need, and send the request to your server. To send requests to localhost or to another server on your local network, you need to add the ReqBin Google Chrome Extension to your browser using this link.

What does a GitHub webhook payload look like?

Every GitHub event payload carries the same envelope — action, sender and repository — around the object the event is about.

GitHub Webhook Example
{
  "action": "opened",
  "issue": {
    "id": 2189470031,
    "number": 142,
    "title": "Timeout on large uploads",
    "state": "open",
    "user": {
      "login": "octocat",
      "id": 583231,
      "type": "User"
    }
  },
  "repository": {
    "id": 186853002,
    "name": "api-client",
    "full_name": "octo-org/api-client",
    "private": false
  },
  "sender": {
    "login": "octocat",
    "id": 583231,
    "type": "User"
  }
}

Two things decide how you read the payload, and only one of them is in it. The X-GitHub-Event header names the event, issues in this example, so your handler can route the delivery before parsing anything. action inside the body then says what happened to the object: opened here, closed, labeled or reopened for other deliveries of the same event. The full list of events and their payloads is in the GitHub webhook reference.

What headers does GitHub add to the request?

Five, on top of the ones any POST carries:

HeaderValue
X-GitHub-Eventissues
X-GitHub-Delivery0b989ba4-242f-11e5-81e1-c7b6966d2516
X-GitHub-Hook-ID292430182
X-Hub-Signature-256sha256=d57c68ca…
User-AgentGitHub-Hookshot/044aadd

The content type is not fixed either. You choose it when you create the webhook: application/json sends the payload as the request body, application/x-www-form-urlencoded sends it form-encoded, and your handler has to read whichever one the webhook is configured for.

How does GitHub sign a webhook?

Your production endpoint URL is public, so anyone who knows or guesses it can post to it. GitHub signs every delivery with the secret token you set on the webhook, and you need to verify the signature before trusting the request. The signature arrives in X-Hub-Signature-256 as an HMAC hex digest of the body, computed with SHA-256 and prefixed with sha256=. Compare it with a constant-time function rather than ==, so the comparison itself leaks nothing.

A second header, X-Hub-Signature, carries the same digest under SHA-1. GitHub keeps it for legacy consumers only; verify against the SHA-256 one.

Does GitHub retry a failed webhook?

No. A delivery fails when your server is down or takes longer than ten seconds to answer, and nothing is sent again unless you ask for it. You resend it by hand from the webhook's Deliveries tab or through the REST API. The X-GitHub-Delivery GUID stays the same across attempts, so deduplicate on it.

Updated: