{"id":15468437,"url":"https://github.com/gr2m/github-webhook-relay","last_synced_at":"2025-04-22T11:28:57.021Z","repository":{"id":64140342,"uuid":"573637387","full_name":"gr2m/github-webhook-relay","owner":"gr2m","description":"Receive webhooks from a GitHub repository via WebSocket","archived":false,"fork":false,"pushed_at":"2025-04-01T22:40:45.000Z","size":42,"stargazers_count":7,"open_issues_count":0,"forks_count":1,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-04-10T10:42:21.187Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"isc","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/gr2m.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null},"funding":{"github":"gr2m"}},"created_at":"2022-12-03T00:39:03.000Z","updated_at":"2025-04-01T22:40:20.000Z","dependencies_parsed_at":"2023-01-24T03:45:23.199Z","dependency_job_id":"c55db1e5-71ad-4bbd-b772-87b4ac5ff670","html_url":"https://github.com/gr2m/github-webhook-relay","commit_stats":{"total_commits":16,"total_committers":1,"mean_commits":16.0,"dds":0.0,"last_synced_commit":"9777f5ec7bd233f5fea319ea22807be18e98d74e"},"previous_names":[],"tags_count":5,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gr2m%2Fgithub-webhook-relay","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gr2m%2Fgithub-webhook-relay/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gr2m%2Fgithub-webhook-relay/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gr2m%2Fgithub-webhook-relay/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/gr2m","download_url":"https://codeload.github.com/gr2m/github-webhook-relay/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":249618017,"owners_count":21300786,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-10-02T01:41:04.350Z","updated_at":"2025-04-22T11:28:56.993Z","avatar_url":"https://github.com/gr2m.png","language":"JavaScript","funding_links":["https://github.com/sponsors/gr2m"],"categories":[],"sub_categories":[],"readme":"# `github-webhook-relay`\n\n\u003e Receive webhooks from a GitHub repository via WebSocket\n\u003e\n\u003e **Warning**  \n\u003e Receiving webhooks via websockets is currently in [private beta](https://github.blog/changelog/2022-11-16-webhook-forwarding-in-the-github-cli-public-beta/)\n\nA Node.js library that uses the same APIs as the [`gh webhook` plugin](https://github.com/cli/gh-webhook) for the [GitHub CLI](https://cli.github.com/).\n\n## Usage\n\nThe `createHookToken` option needs to be set to a [token with the `admin:repo_hook` and/or `admin:org_hook` scope](https://github.com/settings/tokens/new?scopes=admin:repo_hook,admin:org_hook\u0026description=github-webhook-relay), depending on which you want to create.\n\n### Minimal example\n\n```js\nimport WebhookRelay from \"github-webhook-relay\";\n\nconst relay = new WebhookRelay({\n  owner: \"gr2m\",\n  repo: \"github-webhooks-relay\",\n  events: [\"issues\"],\n  createHookToken: process.env.GITHUB_TOKEN,\n});\n\nrelay.on(\"webhook\", ({ id, name, payload, signature, headers }) =\u003e {\n  console.log(\"received webhook: %s\", name);\n});\n\nrelay.on(\"error\", (error) =\u003e {\n  console.log(\"error: %s\", error);\n});\n\nrelay.start();\n```\n\n### Use with Octokit\n\nIf you want to relay webhooks to a GitHub App, you can use [github-app-webhook-relay](https://github.com/gr2m/github-app-webhook-relay).\n\n```js\nimport { App, Octokit } from \"octokit\";\nimport WebhookRelay from \"github-webhook-relay\";\n\nconst MyOctokit = Octokit.defaults({\n  userAgent: \"my-app/1.2.3\",\n});\n\nconst app = new App({\n  appId: process.env.APP_ID,\n  privateKey: process.env.APP_PRIVATE_KEY,\n  webhooks: {\n    secret: process.env.APP_WEBHOOK_SECRET,\n  },\n  Octokit: MyOctokit,\n});\n\napp.webhooks.on(\"issues.opened\", async ({ octokit }) =\u003e {\n  const { data: comment } = await octokit.request(\n    \"POST /repos/{owner}/{repo}/issues/{issue_number}/comments\",\n    {\n      owner: payload.repository.owner.login,\n      repo: payload.repository.name,\n      issue_number: payload.issue.number,\n      body: \"Hello, world!\",\n    }\n  );\n\n  console.log(\"[app] Comment created: %s\", comment.html_url);\n});\n\nconst relay = new WebhookRelay({\n  owner: \"gr2m\",\n  repo: \"github-webhooks-relay\",\n  events: [\"issues\"],\n  octokit: new MyOctokit({ auth: process.env.GITHUB_TOKEN }),\n});\n\nrelay.on(\"webhook\", app.webhooks.verifyAndReceive);\n```\n\n## API\n\n### Constructor\n\n```js\nconst relay = new WebhookRelay(options);\n```\n\n\u003ctable\u003e\n  \u003cthead align=left\u003e\n    \u003ctr\u003e\n      \u003cth\u003e\n        name\n      \u003c/th\u003e\n      \u003cth\u003e\n        type\n      \u003c/th\u003e\n      \u003cth width=100%\u003e\n        description\n      \u003c/th\u003e\n    \u003c/tr\u003e\n  \u003c/thead\u003e\n  \u003ctbody align=left valign=top\u003e\n    \u003ctr\u003e\n      \u003cth\u003e\n        \u003ccode\u003eoptions.owner\u003c/code\u003e\n      \u003c/th\u003e\n      \u003ctd\u003e\n        \u003ccode\u003estring\u003c/code\u003e\n      \u003c/td\u003e\n      \u003ctd\u003e\n\n**Required**. The account name of the GitHub user or organization.\n\n\u003c/td\u003e\n    \u003c/tr\u003e\n    \u003ctr\u003e\n      \u003cth\u003e\n        \u003ccode\u003eoptions.repo\u003c/code\u003e\n      \u003c/th\u003e\n      \u003ctd\u003e\n        \u003ccode\u003estring\u003c/code\u003e\n      \u003c/td\u003e\n      \u003ctd\u003e\n\nWhen set, the webhook will be created for the repository. When not set, the webhook will be created for the organization. Note that user-level webhooks are not supported by GitHub, so `options.owner` must be an organization.\n\n\u003c/td\u003e\n    \u003c/tr\u003e\n    \u003ctr\u003e\n      \u003cth\u003e\n        \u003ccode\u003eoptions.events\u003c/code\u003e\n      \u003c/th\u003e\n      \u003ctd\u003e\n        \u003ccode\u003estring[]\u003c/code\u003e\n      \u003c/td\u003e\n      \u003ctd\u003e\n\n**Required**. The list of events that the webhook should subscribe to. For a list of supported event names, see [the GitHub docs](https://docs.github.com/en/developers/webhooks-and-events/webhooks/webhook-events-and-payloads).\n\n\u003c/td\u003e\n    \u003c/tr\u003e\n    \u003ctr\u003e\n      \u003cth\u003e\n        \u003ccode\u003eoptions.createHookToken\u003c/code\u003e\n      \u003c/th\u003e\n      \u003ctd\u003e\n        \u003ccode\u003estring\u003c/code\u003e\n      \u003c/td\u003e\n      \u003ctd\u003e\n\n**Required unless `options.octokit` is set**. Access token to create the repository webhook. The token needs to have the `admin:repo_hook` scope. ([create a personal access token](https://github.com/settings/tokens/new?scopes=admin:repo_hook\u0026description=github-webhook-relay)).\n\n\u003c/td\u003e\n    \u003c/tr\u003e\n    \u003ctr\u003e\n      \u003cth\u003e\n        \u003ccode\u003eoptions.octokit\u003c/code\u003e\n      \u003c/th\u003e\n      \u003ctd\u003e\n        \u003ccode\u003eoctokit\u003c/code\u003e\n      \u003c/td\u003e\n      \u003ctd\u003e\n\n**Required unless `options.createHookToken` is set**. `octokit` is an instance of [`@octokit/core`](https://github.com/octokit/core.js/#readme) or a compatible constructor such as [`octokit`'s `Octokit`](https://github.com/octokit/octokit.js#octokit-api-client).\n\n\u003c/td\u003e\n    \u003c/tr\u003e\n    \u003ctr\u003e\n      \u003cth\u003e\n        \u003ccode\u003eoptions.webhookSecret\u003c/code\u003e\n      \u003c/th\u003e\n      \u003ctd\u003e\n        \u003ccode\u003estring\u003c/code\u003e\n      \u003c/td\u003e\n      \u003ctd\u003e\n\nThe secret used to sign the webhook payloads. Defaults to no secret.\n\n\u003c/td\u003e\n    \u003c/tr\u003e\n  \u003c/tbody\u003e\n\u003c/table\u003e\n\n### `relay.on()`\n\n```js\nrelay.on(eventName, callback);\n```\n\n\u003ctable\u003e\n  \u003cthead align=left\u003e\n    \u003ctr\u003e\n      \u003cth\u003e\n        name\n      \u003c/th\u003e\n      \u003cth\u003e\n        type\n      \u003c/th\u003e\n      \u003cth width=100%\u003e\n        description\n      \u003c/th\u003e\n    \u003c/tr\u003e\n  \u003c/thead\u003e\n  \u003ctbody align=left valign=top\u003e\n    \u003ctr\u003e\n      \u003cth\u003e\n        \u003ccode\u003eeventName\u003c/code\u003e\n      \u003c/th\u003e\n      \u003ctd\u003e\n        \u003ccode\u003estring\u003c/code\u003e\n      \u003c/td\u003e\n      \u003ctd\u003e\n\n**Required**. Supported events are\n\n1. `webhook` - emitted when a webhook is received\n1. `start` - emitted when the relay is started\n1. `stop` - emitted when the relay is stopped\n1. `error` - emitted when an error occurs\n\n\u003c/td\u003e\n    \u003c/tr\u003e\n    \u003ctr\u003e\n      \u003cth\u003e\n        \u003ccode\u003ecallback\u003c/code\u003e\n      \u003c/th\u003e\n      \u003ctd\u003e\n        \u003ccode\u003efunction\u003c/code\u003e\n      \u003c/td\u003e\n      \u003ctd\u003e\n\n**Required**. The event handler.\n\nWhen `eventName` is `webhook`, the callback is called with an object with the following properties:\n\n- `id` - the webhook delivery GUID\n- `name` - the name of the event\n- `body` - the webhook payload as string\u003csup\u003e†\u003c/sup\u003e\n- `signature` - the signature of the webhook payload\n- `headers` - the headers of the webhook request\n\nNo arguments are passed when `eventName` is set to `start` or `stop`.\n\nWhen `eventName` is `error`, the callback is called with an error object.\n\n\u003csub\u003e†The webhook payload is passed as is in case the signature needs to be verified again. Parsing the JSON and later stringifying it again bight result in a signature mismatch.\u003c/sub\u003e\n\n\u003c/td\u003e\n    \u003c/tr\u003e\n  \u003c/tbody\u003e\n\u003c/table\u003e\n\n### `relay.start()`\n\n```js\nrelay.start();\n```\n\nCreates the repository hook and connects to the GitHub webhook forwarding service.\n\n### `relay.stop()`\n\n```js\nrelay.start();\n```\n\nDisconnects from the GitHub webhook forwarding service and deletes the repository hook.\n\n## How it works\n\nWhen creating a repository webhook using [the `POST /repos/{owner}/{repo}/hooks` endpoint](https://docs.github.com/en/rest/webhooks/repos?apiVersion=2022-11-28#create-a-repository-webhook) and you set `name` to `\"cli\"` then the response body will include a `ws_url` key.\n\nYou can connect to the `ws_url` using a WebSocket client. Note that the handshake request requires an `Authorization` header which will have to be set to a personal access token. Using [the `ws` npm package](https://github.com/websockets/ws#readme) the code looks like this:\n\n```js\nconst ws = new WebSocket(webSocketUrl, {\n  headers: {\n    Authorization: process.env.GITHUB_TOKEN,\n  },\n});\n```\n\nOnce the websocket is connected, the hook can be activated by setting the `active` property to `true` using [the `PATCH /repos/{owner}/{repo}/hooks/{hook_id}` endpoint](https://docs.github.com/en/rest/webhooks/repos?apiVersion=2022-11-28#update-a-repository-webhook).\n\nEach Webhook request is received with a separate message. The message is a JSON string with a `Header` and `Body` keys. The `Body` is base64 encoded.\n\nA response has to be sent back within 10s, otherwise the webhook request will be canceled and time out. No further message will be sent until a response is sent back. The response has to be a JSON string as well with the keys `Status`, `Header`, and `Body`. The `Body` value needs to be base64 encoded.\n\nDisconnecting from the websocket will automatically delete the repository webhook.\n\n## Contributing\n\nPlease see [CONTRIBUTING.md](CONTRIBUTING.md).\n\n## See also\n\n- [`github-webhook-relay-cli`](https://github.com/gr2m/github-webhook-relay-cli/#readme) - CLI version of this library\n- [`github-app-webhook-relay`](https://github.com/gr2m/github-app-webhook-relay/#readme) - Webhook relay for GitHub Apps\n\n## License\n\n[ISC](LICENSE)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgr2m%2Fgithub-webhook-relay","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fgr2m%2Fgithub-webhook-relay","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgr2m%2Fgithub-webhook-relay/lists"}