1---
2title: Webhooks & events
3description: Provision GitHub repository webhooks as resources, or subscribe a Cloudflare Worker to repository events with typed payloads and verified deliveries.
4---
5
6GitHub tells the world what happened to a repository through
7webhooks — `POST`s to a URL on every push, pull request, release,
8and so on. Alchemy gives you two levels of abstraction: the
9`Webhook` resource manages the webhook itself and points it at any
10URL, and `consumeRepositoryEvents` subscribes a Cloudflare Worker to
11a repository with the webhook, routing, and signature verification
12handled for you.
13
14## The Webhook resource
15
16`GitHub.Webhook` manages a repository webhook's lifecycle — created
17on the first deploy, updated in place on subsequent deploys, deleted
18when the stack is destroyed:
19
20```typescript
21import * as GitHub from "alchemy/GitHub";
22
23yield* GitHub.Webhook("ci-webhook", {
24 owner: "my-org",
25 repository: "my-repo",
26 url: "https://example.com/github",
27 events: ["push", "pull_request"],
28 secret,
29});
30```
31
32`events` defaults to `["push"]`; pass `["*"]` to receive every event
33GitHub emits. The `secret` (a `Redacted` string, e.g. from
34`Config.Redacted` — see [Secrets & env](/cloudflare/security/secrets-env))
35makes GitHub sign each delivery with `HMAC-SHA256` in the
36`X-Hub-Signature-256` header so your receiver can verify the payload
37came from GitHub.
38
39Credentials come from `GitHub.providers()` — env, stored PAT, or the
40`gh` CLI (see [setup](/github/setup)). The token needs `repo` scope
41(admin access to the repository) to manage webhooks.
42
43## Point it at another resource
44
45`url` accepts an `Output<string>`, so the webhook can target a
46resource provisioned in the same stack:
47
48```typescript
49const worker = yield* Cloudflare.Worker("Api", { ... });
50
51yield* GitHub.Webhook("repo-webhook", {
52 owner: "my-org",
53 repository: "my-repo",
54 url: worker.url!,
55 events: ["*"],
56});
57```
58
59At this level the receiving end is yours to build: parsing the
60payload, routing the delivery path, and verifying signatures. Most
61of the time you don't want that — read on.
62
63## Consume events from a Worker
64
65`GitHub.consumeRepositoryEvents` subscribes a Cloudflare Worker to a
66repository and replaces all the plumbing: it provisions the webhook
67pointing at the Worker, binds the signing secret onto it, claims the
68delivery path in `fetch`, verifies each delivery's signature, and
69hands your handler a typed payload:
70
71```typescript
72// src/worker.ts
73import * as Cloudflare from "alchemy/Cloudflare";
74import * as GitHub from "alchemy/GitHub";
75import * as Effect from "effect/Effect";
76
77export default Cloudflare.Worker(
78 "Worker",
79 { main: import.meta.url },
80 Effect.gen(function* () {
81 yield* GitHub.consumeRepositoryEvents(
82 {
83 owner: "my-org",
84 repository: "my-repo",
85 events: ["push", "pull_request"],
86 secret,
87 },
88 (event) => {
89 switch (event.name) {
90 case "push":
91 return Effect.log(`push to ${event.payload.ref}`);
92 case "pull_request":
93 return Effect.log(`PR: ${event.payload.pull_request.title}`);
94 }
95 },
96 );
97
98 // ...your own fetch handler is untouched
99 }).pipe(Effect.provide(Cloudflare.Workers.GitHubRepositoryEventSourceLive)),
100);
101```
102
103`event` is Octokit's `EmitterWebhookEvent`, narrowed to the events
104you selected — a `switch` on `event.name` narrows `event.payload`
105exhaustively. The stack provides both clouds' providers:
106`Layer.mergeAll(Cloudflare.providers(), GitHub.providers())`.
107
108For the full walkthrough — runtime layer, secret verification,
109deploy, and a release-blogging agent built on this event source —
110see [React to GitHub events from a Worker](/cloudflare/messaging/github-events).
111
112## Cloudflare-only today
113
114`Cloudflare.Workers.GitHubRepositoryEventSourceLive` is currently
115the only implementation of the `RepositoryEventSource` contract —
116there is no AWS Lambda event source yet. On other hosts you can
117still provision a `GitHub.Webhook` directly with any delivery URL,
118but routing and signature verification are yours to implement.
119
120## Where next
121
122- [React to GitHub events from a Worker](/cloudflare/messaging/github-events)
123 — the goal-oriented guide.
124- [Repositories](/github/repository) — create and converge the repos
125 these webhooks attach to.
126- [GitHub setup](/github/setup) — credentials and token scopes.
127
128Reference:
129
130- [Webhook API reference](/providers/github/webhook)