Overview
Every deployment or release I run has a checklist — environment verified, stakeholders notified, rollback plan confirmed. The problem is those checklists lived in a doc somewhere, disconnected from the actual GitHub PR they were blocking. Engineers had to go find the doc, check the boxes, come back to GitHub, and leave an approval comment. It was tedious and error-prone.
I built this framework to collapse that into a single Slack message. When a checklist needs sign-off, the team gets an interactive Slack message with Approve and Reject buttons. One click does everything: verifies the request, posts a comment on the PR, and triggers whatever GitHub Actions workflow is next in line.
The result: PR approvals that used to take 5–10 minutes of back-and-forth now complete in a single Slack tap, with a full audit trail automatically committed to the repo.
Architecture
Three components, each doing one thing well:
┌──────────────────┐ ┌───────────────────────┐ ┌──────────────────┐
│ Slack App │ ───▶ │ AWS Lambda │ ───▶ │ GitHub API │
│ (Approve / ✕) │ │ slack-approval- │ │ (PR Comments + │
│ │ ◀─── │ handler) │ │ workflow_dispatch) │
└──────────────────┘ └───────────────────────┘ └──────────────────┘
│
Verifies HMAC-SHA256
Slack signature before
any action is taken
Slack handles the user-facing interaction. Lambda is the trusted broker — it validates the request, calls GitHub, and updates the Slack message to reflect the outcome. GitHub Actions does the actual deployment work. Each layer is replaceable independently.
The Stack
GitHub Actions
Two workflows: one that validates checklists on PRs, one that fires on Slack approval.
Slack API
Interactive Block Kit messages with Approve / Reject buttons. HMAC request signing.
AWS Lambda
Python handler that receives Slack payloads, verifies signatures, and calls GitHub.
API Gateway
HTTP endpoint exposed to Slack's interactivity webhook. Routes all button clicks.
YAML Checklists
Human-readable checklist definitions stored in the repo alongside the workflows.
HMAC-SHA256
Every incoming request is signature-verified. Timestamps prevent replay attacks.
Setup Guide
Create a Slack App
Go to api.slack.com/apps and create a new app named
Checklist Approvals. Enable Interactivity & Shortcuts
under Features. Add Bot Token Scopes: chat:write and commands.
Install the app to your workspace and save the Bot Token and
Signing Secret — you'll need both for Lambda.
Deploy the Lambda function
Create a new Lambda function in AWS named slack-approval-handler with a
Python 3.12 runtime. Copy the code from lambda/lambda_function.py
in this repo. Add these environment variables:
# Lambda environment variables
SLACK_SIGNING_SECRET = "..." # From Slack App → Basic Information
GITHUB_TOKEN = "..." # PAT with repo + workflow scopesCreate an API Gateway HTTP API trigger (open security — Slack signature verification happens inside the function). Copy the generated endpoint URL.
Connect Slack to Lambda
In your Slack App settings, go to Interactivity & Shortcuts and set the Request URL to your API Gateway endpoint. Then invite the bot to the channel where approvals will appear:
/invite @Checklist ApprovalsAdd the GitHub Actions workflows
The .github/workflows/ directory contains two workflows.
Copy them into your target repository — no changes needed for basic usage.
# checklist.yml — validates on PR open / update
on:
pull_request:
types: [opened, synchronize]
# approval.yml — fires when Slack Approve is clicked
on:
workflow_dispatch:
inputs:
pr_number:
required: trueSend an approval message
Trigger a Slack approval message by calling the Slack API.
The button value encodes the repo and PR in the format owner:repo:pr_number.
curl -X POST https://slack.com/api/chat.postMessage \
-H "Authorization: Bearer $SLACK_BOT_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"channel": "deployments",
"text": "Checklist Approval Required",
"blocks": [{
"type": "actions",
"elements": [{
"type": "button",
"text": { "type": "plain_text", "text": "Approve ✅" },
"style": "primary",
"action_id": "approve_button",
"value": "joshuaburnie:my-repo:42"
}]
}]
}'End-to-End Flow
What actually happens when someone clicks Approve in Slack:
Button clicked
Slack sends a signed payload to the API Gateway endpoint.
Signature verified
Lambda checks HMAC-SHA256 and rejects requests older than 5 min.
PR comment posted
Lambda calls GitHub API to add an approval comment to the PR.
Workflow triggered
Lambda calls workflow_dispatch on approval.yml.
Slack updated
The original message is updated to reflect Approved / Rejected status.
Artifact saved
Run summary and checklist results are written to artifacts/.
Security
- ✓ HMAC-SHA256 signature verification — every request from Slack is validated using the Signing Secret before any action is taken. Tampered or unsigned requests are rejected immediately.
- ✓ Timestamp replay protection — requests with a timestamp older than 5 minutes are automatically rejected, preventing replay attacks.
- ✓ Secrets in environment variables — no credentials are stored in code or committed to the repository. All secrets live in Lambda's environment.
-
✓
Minimal GitHub token scope — the GitHub PAT only needs
repoandworkflowscopes. Nothing else.
What's Next
The framework is working in production for my security workflows. A few things I'm exploring next:
Multi-approver support — requiring N out of M approvals before the workflow fires, with Slack threads tracking each vote.
Checklist templating — making the YAML checklist definitions richer, with per-step timeouts, owner assignments, and conditional branches.
Slack modal UI — instead of just buttons, opening a Slack modal that shows the full checklist before confirming approval.