Skip to content

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

mermaid
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:

  1. GitHub sends webhooks to Hookbase source endpoint
  2. Source verifies GitHub signature
  3. Routes evaluate events based on x-github-event header
  4. Each route applies custom Slack Block Kit transforms
  5. Formatted messages delivered to respective Slack channels

Step 1: Create the Source

Create a GitHub source with signature verification:

bash
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:

json
{
  "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)

bash
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)

bash
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:

bash
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:

bash
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:

bash
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:

bash
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:

bash
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):

json
{
  "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:

bash
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):

json
{
  "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:

bash
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:

bash
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:

bash
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:

jsonata
{
  "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:

bash
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:

jsonata
{
  "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:

bash
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).

Released under the MIT License.