GitHub → Slack Notifications
This use case demonstrates how to route GitHub webhook events to different Slack channels based on event type. Send pull request events to your #dev channel and deployment events to your #ops channel—each with rich, formatted messages.
Architecture
flowchart LR
GitHub[GitHub Webhooks] --> Source[Hookbase Source]
Source --> R1[Route: PR Events]
Source --> R2[Route: Deploy Events]
R1 --> |Transform| D1["#dev channel"]
R2 --> |Transform| D2["#ops channel"]Flow:
- GitHub sends webhooks to Hookbase source endpoint
- Source verifies GitHub signature
- Routes evaluate events based on
x-github-eventheader - Each route applies custom Slack Block Kit transforms
- Formatted messages delivered to respective Slack channels
Step 1: Create the Source
Create a GitHub source with signature verification:
curl -X POST https://api.hookbase.app/api/organizations/{orgId}/sources \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "GitHub Repository",
"slug": "github-main-repo",
"description": "Main repository webhooks",
"verificationConfig": {
"type": "github",
"secret": "your_webhook_secret_from_github"
}
}'Response:
{
"data": {
"id": "src_github123",
"name": "GitHub Repository",
"slug": "github-main-repo",
"url": "https://api.hookbase.app/ingest/your-org/github-main-repo",
"verificationConfig": {
"type": "github"
},
"createdAt": "2026-02-11T10:00:00Z"
}
}TIP
Configure this URL in GitHub under Settings → Webhooks → Add webhook. Select individual events: Pull requests, Deployments, Deployment statuses.
Step 2: Create Slack Destinations
Create two Slack incoming webhook destinations:
Development Channel (#dev)
curl -X POST https://api.hookbase.app/api/organizations/{orgId}/destinations \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "Slack #dev",
"type": "slack",
"config": {
"url": "https://hooks.slack.com/services/YOUR/DEV/WEBHOOK"
},
"retryConfig": {
"maxAttempts": 2,
"backoffMultiplier": 1.5,
"initialInterval": 500
}
}'Operations Channel (#ops)
curl -X POST https://api.hookbase.app/api/organizations/{orgId}/destinations \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "Slack #ops",
"type": "slack",
"config": {
"url": "https://hooks.slack.com/services/YOUR/OPS/WEBHOOK"
},
"retryConfig": {
"maxAttempts": 2,
"backoffMultiplier": 1.5,
"initialInterval": 500
}
}'TIP
Create incoming webhooks in Slack: Workspace Settings → Manage apps → Incoming Webhooks → Add to Slack.
Step 3: Create Filtered Routes
Pull Request Route
Route PR events to #dev channel:
First, create the filter:
curl -X POST https://api.hookbase.app/api/organizations/{orgId}/filters \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "GitHub PR Events",
"conditions": [
{
"field": "headers.x-github-event",
"operator": "eq",
"value": "pull_request"
},
{
"field": "action",
"operator": "in",
"value": ["opened", "closed", "reopened", "ready_for_review"]
}
],
"logic": "AND"
}'Then, create the route:
curl -X POST https://api.hookbase.app/api/organizations/{orgId}/routes \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "GitHub PRs → #dev",
"sourceId": "src_github123",
"destinationId": "dst_slack_dev123",
"filterId": "flt_pr123",
"transformId": "tfm_pr123",
"enabled": true
}'Deployment Route
Route deployment status events to #ops channel:
First, create the filter:
curl -X POST https://api.hookbase.app/api/organizations/{orgId}/filters \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "GitHub Deployment Status",
"conditions": [
{
"field": "headers.x-github-event",
"operator": "eq",
"value": "deployment_status"
},
{
"field": "deployment_status.state",
"operator": "in",
"value": ["success", "failure", "error"]
}
],
"logic": "AND"
}'Then, create the route:
curl -X POST https://api.hookbase.app/api/organizations/{orgId}/routes \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "GitHub Deploys → #ops",
"sourceId": "src_github123",
"destinationId": "dst_slack_ops123",
"filterId": "flt_deploy123",
"transformId": "tfm_deploy123",
"enabled": true
}'Step 4: Create Transforms
Pull Request Transform
Create a rich Slack Block Kit message for PR events:
curl -X POST https://api.hookbase.app/api/organizations/{orgId}/transforms \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "GitHub PR → Slack Blocks",
"type": "jsonata",
"config": {
"expression": "(\n $pr := pull_request;\n $action := action;\n $isMerged := $pr.merged = true;\n $color := $isMerged ? \"#6f42c1\" : ($action = \"opened\" ? \"#28a745\" : ($action = \"closed\" ? \"#6a737d\" : \"#0366d6\"));\n $emoji := $isMerged ? \"🎉\" : ($action = \"opened\" ? \"🔔\" : ($action = \"closed\" ? \"❌\" : \"🔄\"));\n $actionText := $isMerged ? \"merged\" : action;\n {\n \"attachments\": [\n {\n \"color\": $color,\n \"blocks\": [\n {\n \"type\": \"header\",\n \"text\": {\n \"type\": \"plain_text\",\n \"text\": $emoji & \" Pull Request \" & $actionText\n }\n },\n {\n \"type\": \"section\",\n \"text\": {\n \"type\": \"mrkdwn\",\n \"text\": \"*<\" & $pr.html_url & \"|\" & $pr.title & \">*\\n\" & ($pr.body ? $substring($pr.body, 0, 150) & ($length($pr.body) > 150 ? \"...\" : \"\") : \"_No description provided_\")\n }\n },\n {\n \"type\": \"section\",\n \"fields\": [\n {\n \"type\": \"mrkdwn\",\n \"text\": \"*Author:*\\n<\" & $pr.user.html_url & \"|\" & $pr.user.login & \">\"\n },\n {\n \"type\": \"mrkdwn\",\n \"text\": \"*Repository:*\\n\" & repository.full_name\n },\n {\n \"type\": \"mrkdwn\",\n \"text\": \"*Branch:*\\n\" & $pr.head.ref & \" → \" & $pr.base.ref\n },\n {\n \"type\": \"mrkdwn\",\n \"text\": \"*Status:*\\n\" & $pr.state & ($isMerged ? \" (merged)\" : \"\")\n }\n ]\n },\n {\n \"type\": \"context\",\n \"elements\": [\n {\n \"type\": \"mrkdwn\",\n \"text\": \"PR #\" & $string($pr.number) & \" | +\" & $string($pr.additions) & \" -\" & $string($pr.deletions) & \" | \" & $string($pr.changed_files) & \" files changed\"\n }\n ]\n },\n {\n \"type\": \"actions\",\n \"elements\": [\n {\n \"type\": \"button\",\n \"text\": {\n \"type\": \"plain_text\",\n \"text\": \"View PR\"\n },\n \"url\": $pr.html_url,\n \"style\": \"primary\"\n },\n {\n \"type\": \"button\",\n \"text\": {\n \"type\": \"plain_text\",\n \"text\": \"View Diff\"\n },\n \"url\": $pr.diff_url\n }\n ]\n }\n ]\n }\n ]\n }\n)"
}
}'Example Output (Slack Message):
{
"attachments": [
{
"color": "#28a745",
"blocks": [
{
"type": "header",
"text": {
"type": "plain_text",
"text": "🔔 Pull Request opened"
}
},
{
"type": "section",
"text": {
"type": "mrkdwn",
"text": "*<https://github.com/company/repo/pull/123|Add new analytics dashboard>*\nImplements a new analytics dashboard with real-time metrics visualization. Includes chart components, API integrations, and dat..."
}
},
{
"type": "section",
"fields": [
{
"type": "mrkdwn",
"text": "*Author:*\n<https://github.com/johndoe|johndoe>"
},
{
"type": "mrkdwn",
"text": "*Repository:*\ncompany/repo"
},
{
"type": "mrkdwn",
"text": "*Branch:*\nfeature/analytics → main"
},
{
"type": "mrkdwn",
"text": "*Status:*\nopen"
}
]
},
{
"type": "context",
"elements": [
{
"type": "mrkdwn",
"text": "PR #123 | +450 -120 | 12 files changed"
}
]
},
{
"type": "actions",
"elements": [
{
"type": "button",
"text": {
"type": "plain_text",
"text": "View PR"
},
"url": "https://github.com/company/repo/pull/123",
"style": "primary"
},
{
"type": "button",
"text": {
"type": "plain_text",
"text": "View Diff"
},
"url": "https://github.com/company/repo/pull/123.diff"
}
]
}
]
}
]
}Color Legend:
- Green (#28a745) - PR opened
- Purple (#6f42c1) - PR merged
- Gray (#6a737d) - PR closed without merge
- Blue (#0366d6) - PR reopened or ready for review
Deployment Transform
Create a rich Slack Block Kit message for deployment events:
curl -X POST https://api.hookbase.app/api/organizations/{orgId}/transforms \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "GitHub Deploy → Slack Blocks",
"type": "jsonata",
"config": {
"expression": "(\n $deploy := deployment;\n $status := deployment_status;\n $isSuccess := $status.state = \"success\";\n $color := $isSuccess ? \"#28a745\" : \"#d73a49\";\n $emoji := $isSuccess ? \"✅\" : \"🚨\";\n $statusText := $isSuccess ? \"succeeded\" : \"failed\";\n {\n \"attachments\": [\n {\n \"color\": $color,\n \"blocks\": [\n {\n \"type\": \"header\",\n \"text\": {\n \"type\": \"plain_text\",\n \"text\": $emoji & \" Deployment \" & $statusText\n }\n },\n {\n \"type\": \"section\",\n \"text\": {\n \"type\": \"mrkdwn\",\n \"text\": \"*Environment:* \" & $deploy.environment & \"\\n\" & ($status.description ? $status.description : \"\")\n }\n },\n {\n \"type\": \"section\",\n \"fields\": [\n {\n \"type\": \"mrkdwn\",\n \"text\": \"*Repository:*\\n\" & repository.full_name\n },\n {\n \"type\": \"mrkdwn\",\n \"text\": \"*Commit:*\\n<\" & $deploy.payload.web_url & \"|\" & $substring($deploy.sha, 0, 7) & \">\"\n },\n {\n \"type\": \"mrkdwn\",\n \"text\": \"*Deployer:*\\n<\" & $deploy.creator.html_url & \"|\" & $deploy.creator.login & \">\"\n },\n {\n \"type\": \"mrkdwn\",\n \"text\": \"*Status:*\\n\" & $status.state\n }\n ]\n },\n $status.log_url ? {\n \"type\": \"section\",\n \"text\": {\n \"type\": \"mrkdwn\",\n \"text\": \"*Logs:* <\" & $status.log_url & \"|View deployment logs>\"\n }\n },\n {\n \"type\": \"context\",\n \"elements\": [\n {\n \"type\": \"mrkdwn\",\n \"text\": \"Deployment ID: \" & $string($deploy.id) & \" | Triggered at \" & $fromMillis($toMillis($deploy.created_at), \"[h]:[m01] [P]\", \"+00:00\")\n }\n ]\n },\n {\n \"type\": \"actions\",\n \"elements\": [\n $status.target_url ? {\n \"type\": \"button\",\n \"text\": {\n \"type\": \"plain_text\",\n \"text\": \"View Deployment\"\n },\n \"url\": $status.target_url,\n \"style\": $isSuccess ? \"primary\" : \"danger\"\n },\n {\n \"type\": \"button\",\n \"text\": {\n \"type\": \"plain_text\",\n \"text\": \"View Commit\"\n },\n \"url\": $deploy.payload.web_url\n }\n ]\n }\n ]\n }\n ]\n }\n)"
}
}'Example Output (Slack Message - Success):
{
"attachments": [
{
"color": "#28a745",
"blocks": [
{
"type": "header",
"text": {
"type": "plain_text",
"text": "✅ Deployment succeeded"
}
},
{
"type": "section",
"text": {
"type": "mrkdwn",
"text": "*Environment:* production\nDeployment completed successfully. All health checks passed."
}
},
{
"type": "section",
"fields": [
{
"type": "mrkdwn",
"text": "*Repository:*\ncompany/repo"
},
{
"type": "mrkdwn",
"text": "*Commit:*\n<https://github.com/company/repo/commit/abc1234|abc1234>"
},
{
"type": "mrkdwn",
"text": "*Deployer:*\n<https://github.com/johndoe|johndoe>"
},
{
"type": "mrkdwn",
"text": "*Status:*\nsuccess"
}
]
},
{
"type": "section",
"text": {
"type": "mrkdwn",
"text": "*Logs:* <https://logs.example.com/deployment/123|View deployment logs>"
}
},
{
"type": "context",
"elements": [
{
"type": "mrkdwn",
"text": "Deployment ID: 987654321 | Triggered at 2:30 PM"
}
]
},
{
"type": "actions",
"elements": [
{
"type": "button",
"text": {
"type": "plain_text",
"text": "View Deployment"
},
"url": "https://app.example.com/deployments/123",
"style": "primary"
},
{
"type": "button",
"text": {
"type": "plain_text",
"text": "View Commit"
},
"url": "https://github.com/company/repo/commit/abc1234"
}
]
}
]
}
]
}Color Legend:
- Green (#28a745) - Deployment success
- Red (#d73a49) - Deployment failure or error
Step 5: Test the Pipeline
Send a mock GitHub PR event to test the flow:
curl -X POST https://api.hookbase.app/ingest/your-org/github-main-repo \
-H "Content-Type: application/json" \
-H "X-GitHub-Event: pull_request" \
-H "X-Hub-Signature-256: sha256=test_signature" \
-H "X-GitHub-Delivery: test-delivery-123" \
-d '{
"action": "opened",
"number": 123,
"pull_request": {
"id": 1234567890,
"number": 123,
"state": "open",
"title": "Add new analytics dashboard",
"body": "Implements a new analytics dashboard with real-time metrics visualization. Includes chart components, API integrations, and data export functionality.",
"user": {
"login": "johndoe",
"html_url": "https://github.com/johndoe"
},
"html_url": "https://github.com/company/repo/pull/123",
"diff_url": "https://github.com/company/repo/pull/123.diff",
"head": {
"ref": "feature/analytics"
},
"base": {
"ref": "main"
},
"merged": false,
"additions": 450,
"deletions": 120,
"changed_files": 12
},
"repository": {
"full_name": "company/repo",
"html_url": "https://github.com/company/repo"
}
}'TIP
For real GitHub events, use the GitHub CLI: gh api repos/{owner}/{repo}/hooks to list webhooks, then trigger events with gh pr create or actual repository actions.
Verify in Slack: Check your #dev channel for the formatted PR notification.
Verify Delivery:
curl https://api.hookbase.app/api/organizations/{orgId}/deliveries?limit=5 \
-H "Authorization: Bearer YOUR_TOKEN"You should see a successful delivery to the #dev Slack destination with the transformed payload.
Extensions
Security Alerts (#security)
Add a route for security advisory events:
Filter:
curl -X POST https://api.hookbase.app/api/organizations/{orgId}/filters \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "GitHub Security Events",
"conditions": [
{
"field": "headers.x-github-event",
"operator": "in",
"value": ["security_advisory", "code_scanning_alert", "dependabot_alert"]
}
],
"logic": "AND"
}'Transform with red color and alert emoji:
{
"attachments": [{
"color": "#d73a49",
"blocks": [
{
"type": "header",
"text": {
"type": "plain_text",
"text": "🚨 Security Alert"
}
},
{
"type": "section",
"text": {
"type": "mrkdwn",
"text": "*" & alert.rule.description & "*\nSeverity: " & $uppercase(alert.rule.severity)
}
}
]
}]
}Release Notifications (#releases)
Add a route for release events:
Filter:
curl -X POST https://api.hookbase.app/api/organizations/{orgId}/filters \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "GitHub Release Events",
"conditions": [
{
"field": "headers.x-github-event",
"operator": "eq",
"value": "release"
},
{
"field": "action",
"operator": "eq",
"value": "published"
}
],
"logic": "AND"
}'Transform with celebration emoji:
{
"attachments": [{
"color": "#0366d6",
"blocks": [
{
"type": "header",
"text": {
"type": "plain_text",
"text": "🎉 New Release: " & release.tag_name
}
},
{
"type": "section",
"text": {
"type": "mrkdwn",
"text": "*<" & release.html_url & "|" & release.name & ">*\n" & release.body
}
}
]
}]
}Circuit Breaker for Slack
Prevent Slack rate limiting during high-volume events:
curl -X PATCH https://api.hookbase.app/api/organizations/{orgId}/destinations/dst_slack_dev123 \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"circuitBreakerConfig": {
"enabled": true,
"failureThreshold": 5,
"windowSeconds": 60,
"resetTimeoutSeconds": 300
}
}'This prevents hammering Slack if GitHub sends a burst of events (e.g., batch PR updates).
Related Guides
- GitHub Integration - Provider-specific setup and signature verification
- Slack Integration - Slack incoming webhooks and Block Kit
- Transforms - JSONata syntax and Slack Block Kit patterns
- Filters - Header-based routing and complex conditions
- Circuit Breaker - Protect Slack from rate limiting
- Notification Channels - Get alerted to delivery failures