Monday GitHub Integration - Implementation Guide
This guide is for developers implementing or maintaining the Monday-GitHub integration. For user-facing documentation, see Monday GitHub Integration.
Architecture
System Overview
Components
-
Local Git Hook (Optional)
- File:
.github/hooks/commit-msg - Validates commit messages contain Monday links
- Installed manually by running
.github/hooks/install.sh - Allows bypass with
[skip-monday]tag or--no-verifyflag
- File:
-
GitHub Webhook
- Triggers on push events to any branch
- Sends payload to pivot-kpi API endpoint
- Configured via Terraform in pivot-devops
-
pivot-kpi API Endpoint (
/api/webhooks/github)- Receives and processes webhook payloads
- Verifies webhook signature (security)
- Extracts Monday task IDs from commit messages
-
Monday.com API Client
- Posts updates to Monday tasks
- Includes duplicate detection logic
- Formats commit info for Monday
Setup Guide for New Repositories
Step 1: Add Git Hooks to Repository
Create .github/hooks/commit-msg:
#!/bin/bash
# Monday.com Link Validation - Local Git Hook
# Validates that commit messages contain a Monday.com task link
#
# Installation: Run ./.github/hooks/install.sh
#
# Bypass:
# - Use [skip-monday] in commit message
# - Use git commit --no-verify to skip all hooks
COMMIT_MSG_FILE=$1
COMMIT_MSG=$(cat "$COMMIT_MSG_FILE")
# Check if commit message contains Monday.com link or [skip-monday] tag
if echo "$COMMIT_MSG" | grep -qE '(monday\.com/boards/[0-9]+(/views/[0-9]+)?/pulses/[0-9]+|\[skip-monday\])'; then
# Valid commit - has Monday link or skip tag
exit 0
else
# Invalid commit - missing Monday link
echo ""
echo "❌ COMMIT REJECTED: Missing Monday.com task link"
echo ""
echo "Every commit must include a Monday.com task URL like:"
echo " https://pivotapp.monday.com/boards/XXX/pulses/YYY"
echo ""
echo "To bypass this check (for testing/CICD), add [skip-monday] to your commit message."
echo "To skip this hook entirely, use: git commit --no-verify"
echo ""
exit 1
fi
Create .github/hooks/install.sh:
#!/bin/bash
# Install Monday.com validation git hook
# Run this once after cloning the repository
HOOK_SOURCE=".github/hooks/commit-msg"
HOOK_DEST=".git/hooks/commit-msg"
if [ ! -f "$HOOK_SOURCE" ]; then
echo "❌ Error: $HOOK_SOURCE not found"
echo "Make sure you're running this from the repository root"
exit 1
fi
# Copy hook to git hooks directory
cp "$HOOK_SOURCE" "$HOOK_DEST"
chmod +x "$HOOK_DEST"
echo "✅ Git hook installed successfully!"
echo ""
echo "The commit-msg hook will now validate that all commits include a Monday.com task link."
echo ""
echo "Usage:"
echo " - Normal commit: git commit -m \"Your message"
echo ""
echo " https://pivotapp.monday.com/boards/XXX/pulses/YYY\""
echo " - Skip validation: git commit -m \"Your message [skip-monday]\""
echo " - Skip all hooks: git commit --no-verify -m \"Your message\""
echo ""
Make executable and commit:
chmod +x .github/hooks/commit-msg .github/hooks/install.sh
git add .github/hooks/
git commit -m "Add Monday.com validation git hooks
https://pivotapp.monday.com/boards/XXX/pulses/YYY"
git push
Step 2: Configure Webhook via Terraform
In /home/chipdev/pivot-meta/pivot-devops/github/webhook-[your-repo].tf:
# GitHub Webhook for [your-repo]
# Sends push events to the KPI dashboard for Monday.com integration
resource "github_repository_webhook" "your_repo_monday_integration" {
repository = "your-repo-name"
configuration {
url = "https://kpi.pivotdev.ca/api/webhooks/github"
content_type = "json"
secret = "c6ef29d6bd781b265c6fe59445bd05ee9b047deaadb9e934361c8aaeef2980bc"
insecure_ssl = false
}
events = ["push", "pull_request"]
active = true
}
Apply with:
cd /home/chipdev/pivot-meta/pivot-devops/github
terraform apply
Step 3: Test the Integration
Push a commit with a Monday link:
git commit -m "Test Monday integration
https://pivotapp.monday.com/boards/4176868787/pulses/18382176781"
git push origin main
Check:
- Monday task for notification
- GitHub webhook deliveries (Settings → Webhooks)
Monday.com URL Pattern
The system detects Monday task references in commit messages using this regex:
/monday\.com\/boards\/\d+(?:\/views\/\d+)?\/pulses\/(\d+)/
Matches:
https://pivotapp.monday.com/boards/123/pulses/456https://pivotapp.monday.com/boards/123/views/789/pulses/456
Duplicate Detection
Problem
When a PR is merged, the same commit can appear multiple times in the target branch, which would create duplicate notifications.
Solution
Before posting a notification, check if an identical update already exists in the Monday task:
- Fetch existing updates for the task via Monday API
- Compare the new notification text with existing updates
- Only post if no exact match exists
Edge Cases
- Different commits, same PR: Allow duplicates (different commit messages)
- Same commit, different tasks: Allow duplicates (different Monday tasks)
- Same commit, same task: Prevent duplicate
Pull Request Tracking
In addition to commit tracking, the system now supports full pull request lifecycle management with Monday.com integration.
PR Workflow
Commit Tracking Flow
Key Points:
- Only commits pushed to
mainormasterbranches trigger Monday updates - Feature branch commits are tracked via PR notifications instead
- Commits are accumulated into a single Monday update that gets replaced when new commits arrive
- Each commit update shows: commit SHA (short), message, author, and link to GitHub
PR Requirements
All Pull Requests MUST include a Monday.com task link in the PR body:
https://pivotapp.monday.com/boards/BOARD_ID/pulses/TASK_ID
or
https://pivot584586.monday.com/boards/BOARD_ID/pulses/TASK_ID
The Monday link MUST be in the PR description body, NOT in individual commit messages.
PR Events Handled
-
PR Opened (
pull_request.opened)- Extracts Monday task IDs from PR body
- Posts notification to each linked Monday task
- Notification includes: PR number, title, author, link
-
PR Merged (
pull_request.closedwithmerged: true)- Extracts Monday task IDs from PR body
- Updates BOTH status columns on the Monday task
- Posts merge notification
-
PR Closed (not merged) (
pull_request.closedwithmerged: false)- No action taken
-
Other PR events (
edited,reopened,synchronize)- No action taken (avoids spam)
Status Column Updates
When a PR is merged, the system automatically updates TWO status columns on the linked Monday task(s):
| Column ID | Column Name | New Value |
|---|---|---|
status9 | Status | Deployed to non-production |
dup__of_status__1 | Deployment Status | Deployed to non-production |
Implementation Details:
- Requires
board_idparameter (fetched viagetTaskDetails()) - Uses Monday GraphQL mutation
change_column_value - Value format:
JSON.stringify({ label: "Deployed to non-production" })
Preview URL Integration
For repositories with Firebase Hosting preview deployments (like pivot), the preview URL is automatically posted to Monday.
Preview URL Format: https://pivot-dev-59310--pr-{number}-{hash}.web.app
Implementation Details:
-
Firebase Workflow (
.github/workflows/firebase-hosting-pull-request.yml):- Deploy to preview channel captures
details_urloutput - Workflow calls pivot-kpi endpoint with preview URL and PR details
- Deploy to preview channel captures
-
pivot-kpi Endpoint (
/api/webhooks/monday-preview):- Receives preview URL, PR number, repository, and PR body
- Extracts Monday task IDs from PR body
- Checks for duplicate preview URLs (prevents reposting)
- Posts clickable HTML link to Monday task
-
Notification Format:
🌐 Preview Deployment
PR #123 - pivotteam/pivot
🔗 Preview URL: <a href="URL" target="_blank">URL</a>
Duplicate Prevention: The system checks existing Monday updates for the same preview URL before posting, preventing duplicates if the workflow reruns.
Monday Updates Summary
For each PR, you'll see up to 4 separate Monday updates:
- PR Opened - When PR is created (one time)
- Commits - Consolidated list of commits on main branch (updated as new commits pushed)
- Preview URL - Link to preview deployment (posted once, if available)
- PR Merged - Merge notification + status column updates (one time)
Important: Each update is separate and distinct to maintain clarity in Monday task history.
API Endpoints
GitHub Webhook Receiver
Endpoint: POST /api/webhooks/github
Location: /home/chipdev/pivot-meta/pivot-kpi/app/api/webhooks/github/route.ts
Authentication: GitHub webhook secret (HMAC SHA-256)
Payload (GitHub push event):
{
"ref": "refs/heads/branch-name",
"commits": [
{
"id": "abc123...",
"message": "Fix bug\n\nhttps://pivotapp.monday.com/boards/123/pulses/456",
"url": "https://github.com/org/repo/commit/abc123",
"author": {
"name": "Developer Name",
"email": "dev@example.com"
}
}
],
"repository": {
"name": "repo-name",
"full_name": "org/repo-name"
}
}
Response:
{
"success": true,
"repository": "pivotteam/pivot",
"branch": "main",
"commits_processed": 2,
"notifications": {
"posted": 2,
"duplicates_skipped": 0,
"errors": 0
},
"results": [
{
"taskId": "456",
"commit": "abc123",
"status": "posted"
}
]
}
Monday.com API Integration
Authentication
Use Monday.com API token (personal or app token).
Environment Variable: MONDAY_API_TOKEN
Token Location:
- GitHub: Organization secret
MONDAY_API_TOKEN - pivot-kpi: Environment variable or GitHub Actions secret
API Endpoints Used
-
Get Task Updates
- Endpoint:
https://api.monday.com/v2 - Query:
query { items(ids: [TASK_ID]) { updates { body } } } - Purpose: Check for duplicates
- Endpoint:
-
Create Update
- Endpoint:
https://api.monday.com/v2 - Mutation:
mutation { create_update(item_id: TASK_ID, body: "text") { id } } - Purpose: Post notification
- Endpoint:
Notification Format
🔧 GitHub Commit
Repository: org/repo-name
Branch: feature-branch
Commit: abc123
Message: Fix authentication bug
👤 Author: Developer Name
🔗 View Commit: https://github.com/org/repo/commit/abc123
Implementation Checklist
Core Infrastructure (One-time - COMPLETED)
- Create Monday.com API token
- Create organization-level
MONDAY_API_TOKENsecret - Implement GitHub webhook receiver at
/api/webhooks/github - Implement Monday.com client wrapper
- Implement duplicate detection logic
- Add webhook signature verification
- Deploy to production (kpi.pivotdev.ca)
Adding to New Repository
- Create
.github/hooks/commit-msghook file - Create
.github/hooks/install.shinstaller script - Make hook files executable (
chmod +x) - Commit hook files to repository
- Add webhook configuration in Terraform (
webhook-*.tf) - Run
terraform apply - Test: Push commit with Monday link
- Verify: Check Monday task for notification
- Verify: Check GitHub webhook deliveries
- Document: Update repository README with hook installation instructions
- Inform: Tell team to run
./.github/hooks/install.shafter cloning
Current Status
Commit Tracking + PR Tracking:
- pivot (main web app - webhook + hooks + PR tracking + preview URLs)
- pivot-kpi (KPI dashboard - webhook + hooks + PR tracking)
- pivot-mobile (React Native app - webhook + hooks + PR tracking)
- pivot-jobs (pending)
Features Implemented:
- Commit notifications to Monday (main branch only)
- PR opened notifications
- PR merged notifications with status updates
- Automatic status column updates (Status + Deployment Status)
- Preview URL posting with clickable links
- Comprehensive unit tests with CI/CD integration
Troubleshooting
Webhook not posting to Monday
-
Check webhook delivery in GitHub:
- Go to repository Settings → Webhooks
- Click on webhook URL
- Check recent deliveries for errors
-
Verify webhook secret matches:
- Terraform: Check
webhook-*.tffiles - pivot-kpi: Check
WEBHOOK_SECRETenvironment variable
- Terraform: Check
-
Check pivot-kpi logs (webhook receiver):
cd /home/chipdev/pivot-meta/pivot-kpi
npm run dev # Check console for webhook events -
Test webhook manually:
# Get recent delivery payload from GitHub
# Resend using curl or Postman
Webhook secret mismatch
All webhooks use the same secret:
c6ef29d6bd781b265c6fe59445bd05ee9b047deaadb9e934361c8aaeef2980bc
This must match the WEBHOOK_SECRET environment variable in pivot-kpi.
Security Considerations
-
Webhook Secret Verification
- Always verify GitHub webhook signature
- Use constant-time comparison
- Reject invalid signatures immediately
-
API Token Storage
- Store in environment variables
- Never commit to git
- Rotate periodically
-
Input Validation
- Sanitize commit messages before posting
- Validate Monday task IDs (numeric only)
- Prevent injection attacks
Monitoring
Metrics to Track
- Total webhooks received
- Notifications posted successfully
- Duplicates skipped
- Errors by type
- Processing time
- Monday API rate limit usage
Logs to Capture
{
timestamp: "2025-01-18T12:00:00Z",
event: "webhook_received",
repository: "pivotteam/pivot",
branch: "main",
commits: 3,
notifications: {
posted: 2,
duplicates_skipped: 1,
errors: 0
}
}
Rate Limits
Monday.com API
- Rate limit: 60 requests per minute per token
- Recommended: Add exponential backoff and retry logic
- Monitor: Check response headers for rate limit status
GitHub Webhooks
- Timeout: 10 seconds
- Retry: GitHub retries failed webhooks
- Recommended: Respond quickly (200 OK), process async if needed
File Locations
Git Hooks (in each repository)
.github/hooks/commit-msg- Validation hook.github/hooks/install.sh- Installation script
Terraform (pivot-devops repository)
github/webhook-kpi.tf- pivot-kpi webhookgithub/webhook-pivot.tf- pivot webhookgithub/webhook-mobile.tf- Pivotmobile webhook
API Implementation (pivot-kpi repository)
app/api/webhooks/github/route.ts- Webhook receiver (handles both push and pull_request events)app/api/webhooks/monday-preview/route.ts- Preview URL posting endpointlib/github/webhooks.ts- GitHub utilities and type definitionslib/github/pr-handlers.ts- Pull request event handlerslib/monday/client.ts- Monday.com API client (includes status update functions)tests/unit/- Comprehensive unit tests for all pure functionstests/unit/monday-preview-endpoint.test.ts- Preview URL endpoint tests
Future Enhancements
-
Bidirectional Sync
- Monday task updates → GitHub PR comments
- Status sync (task complete → close PR)
-
Rich Notifications
- Include diff summary
- Link to CI/CD status
- Show changed files
-
Analytics Dashboard
- Commits per Monday task
- Developer activity
- Task completion velocity
-
Automated Hook Installation
- Explore git config options (init.templateDir)
- Consider package.json postinstall script
- Document for team leads