Skip to main content
Connect GitHub repositories to Hazel Chat channels and receive real-time notifications for commits, pull requests, issues, releases, and workflow runs.

Overview

The GitHub integration allows you to:
  • Subscribe channels to specific repositories
  • Filter events by type (push, pull_request, issues, release, deployment_status, workflow_run)
  • Filter branches for push events (e.g., only main branch)
  • Enable/disable subscriptions without deleting them
Events are posted to subscribed channels as messages from the GitHub integration bot.

Setup

1

Install the GitHub App

Install the Hazel GitHub App for your organization:
  1. Go to Settings → Integrations → GitHub
  2. Click “Connect GitHub”
  3. Select your GitHub organization
  4. Choose which repositories to grant access to
  5. Complete the installation
The app will be linked to your Hazel organization via the GitHub installation ID.
2

Create a Subscription

Subscribe a channel to a repository:
import { GitHubSubscriptionRpcs } from "@hazel/domain/rpc"

const { data: subscription } = yield* rpc.githubSubscription.create({
  channelId: "channel_abc123",
  repositoryId: 123456789,
  repositoryFullName: "acme/api",
  repositoryOwner: "acme",
  repositoryName: "api",
  enabledEvents: [
    "push",
    "pull_request",
    "issues",
    "release"
  ],
  branchFilter: "main", // Optional: only notify for main branch
})
3

Configure Event Filters

Update which events trigger notifications:
const { data: subscription } = yield* rpc.githubSubscription.update({
  id: subscriptionId,
  enabledEvents: ["pull_request", "release"],
  branchFilter: null, // All branches
})

Supported Events

The GitHub integration supports the following event types:
Event TypeDescriptionBranch Filter
pushCommits pushed to repositoryYes
pull_requestPR opened, updated, merged, or closedNo
issuesIssue opened, updated, or closedNo
releaseRelease publishedNo
deployment_statusDeployment status changedNo
workflow_runGitHub Actions workflow completedNo

Event Filtering Example

// Only notify for PRs and releases
enabledEvents: ["pull_request", "release"]

// Notify for all event types
enabledEvents: [
  "push",
  "pull_request",
  "issues",
  "release",
  "deployment_status",
  "workflow_run"
]

Webhook Processing

GitHub webhooks are processed by the cluster service using a durable workflow:
packages/domain/src/cluster/workflows/github-webhook-workflow.ts:5-28
export const GitHubWebhookWorkflow = Workflow.make({
  name: "GitHubWebhookWorkflow",
  payload: {
    // GitHub delivery ID for idempotency
    deliveryId: Schema.String,
    // Event type (push, pull_request, etc.)
    eventType: Schema.String,
    // Installation ID to find the organization
    installationId: Schema.Number,
    // Repository identification
    repositoryId: Schema.Number,
    repositoryFullName: Schema.String,
    // Full GitHub event payload
    eventPayload: Schema.Unknown,
  },
  error: GitHubWebhookWorkflowError,
  // Use delivery ID for idempotency
  idempotencyKey: (payload) => payload.deliveryId,
})
The workflow:
  1. Receives webhook event from GitHub
  2. Finds all subscriptions for the repository
  3. Filters by enabled events and branch (if applicable)
  4. Creates formatted messages in subscribed channels
  5. Uses the delivery ID for idempotency (duplicate deliveries are ignored)

Managing Subscriptions

List Subscriptions

const { data: subscriptions } = yield* rpc.githubSubscription.list({
  channelId: "channel_abc123",
})

Update Subscription

const { data: subscription } = yield* rpc.githubSubscription.update({
  id: subscriptionId,
  enabledEvents: ["push", "pull_request"],
  branchFilter: "main",
  isEnabled: true,
})

Disable Subscription

Temporarily disable without deleting:
const { data: subscription } = yield* rpc.githubSubscription.update({
  id: subscriptionId,
  isEnabled: false,
})

Delete Subscription

const { transactionId } = yield* rpc.githubSubscription.delete({
  id: subscriptionId,
})

Message Format

GitHub events are posted as formatted messages with rich content:

Push Event

[acme/api] 3 new commits to main

John Doe:
- Fix authentication bug (#123)
- Update dependencies
- Add rate limiting

https://github.com/acme/api/compare/abc123...def456

Pull Request Event

[acme/api] Pull Request #42 opened by jane-dev

Add user profile API

Implements user profile endpoints with caching and validation.

https://github.com/acme/api/pull/42

Release Event

[acme/api] Release v2.1.0 published

Bug fixes and performance improvements

- Fix memory leak in websocket handler
- Improve database query performance
- Update to latest Effect-TS

https://github.com/acme/api/releases/tag/v2.1.0

RPC Schema Reference

Create Subscription

packages/domain/src/rpc/github-subscriptions.ts:84-102
Rpc.make("githubSubscription.create", {
  payload: Schema.Struct({
    channelId: ChannelId,
    repositoryId: Schema.Number,
    repositoryFullName: Schema.String,
    repositoryOwner: Schema.String,
    repositoryName: Schema.String,
    enabledEvents: GitHubSubscription.GitHubEventTypes,
    branchFilter: Schema.optional(Schema.NullOr(Schema.String)),
  }),
  success: GitHubSubscriptionResponse,
  error: Schema.Union(
    ChannelNotFoundError,
    GitHubNotConnectedError,
    GitHubSubscriptionExistsError,
    UnauthorizedError,
    InternalServerError,
  ),
}).middleware(AuthMiddleware)

Update Subscription

packages/domain/src/rpc/github-subscriptions.ts:145-154
Rpc.make("githubSubscription.update", {
  payload: Schema.Struct({
    id: GitHubSubscriptionId,
    enabledEvents: Schema.optional(GitHubSubscription.GitHubEventTypes),
    branchFilter: Schema.optional(Schema.NullOr(Schema.String)),
    isEnabled: Schema.optional(Schema.Boolean),
  }),
  success: GitHubSubscriptionResponse,
  error: Schema.Union(GitHubSubscriptionNotFoundError, UnauthorizedError, InternalServerError),
}).middleware(AuthMiddleware)

Error Handling

Common Errors

ErrorCauseSolution
GitHubNotConnectedErrorGitHub App not installedInstall the Hazel GitHub App for your organization
GitHubSubscriptionExistsErrorChannel already subscribed to repositoryUpdate the existing subscription or delete it first
ChannelNotFoundErrorInvalid channel IDVerify the channel exists and you have access
UnauthorizedErrorUser not authorizedMust be organization admin to manage subscriptions

Error Example

const result = yield* rpc.githubSubscription.create({
  channelId: "channel_abc123",
  repositoryId: 123456789,
  // ...
}).pipe(
  Effect.catchTag("GitHubNotConnectedError", (error) =>
    Effect.gen(function* () {
      yield* Effect.log("GitHub not connected for this organization")
      // Show instructions to install GitHub App
    })
  ),
  Effect.catchTag("GitHubSubscriptionExistsError", (error) =>
    Effect.gen(function* () {
      yield* Effect.log(`Already subscribed: ${error.repositoryId}`)
      // Fetch and update existing subscription
    })
  ),
)

Best Practices

1

Use Dedicated Channels

Create separate channels for different repositories or event types:
  • #engineering-api-releases - Only release events
  • #engineering-api-prs - Only PR events
  • #deployments - Only deployment_status and workflow_run events
2

Filter Events Carefully

Only enable events that are relevant to avoid notification fatigue:
// Good: Only critical events
enabledEvents: ["release", "deployment_status"]

// Bad: All events (high volume)
enabledEvents: ["push", "pull_request", "issues", "release", "deployment_status", "workflow_run"]
3

Use Branch Filters

For push events, filter to important branches:
branchFilter: "main" // Only notify for main branch
branchFilter: "production" // Only notify for production branch
branchFilter: null // All branches (high volume)
4

Disable Instead of Delete

Temporarily disable subscriptions instead of deleting:
// Disable during maintenance
yield* rpc.githubSubscription.update({
  id: subscriptionId,
  isEnabled: false,
})

// Re-enable later
yield* rpc.githubSubscription.update({
  id: subscriptionId,
  isEnabled: true,
})

Next Steps

RSS Feeds

Subscribe to RSS feeds for automated content

Webhooks

Create incoming webhooks for external notifications

Build docs developers (and LLMs) love