A Slack-hosted Deno app that automatically approves Slack Connect invite requests for private channels and creates audit tickets in Jira. Public channel invites are left in the governance queue for manual review. Inbound invites (external orgs inviting company users) are also tracked with Jira tickets.
- A user sends a Slack Connect invitation from a private channel
- Slack routes the request through the governance approval queue
- The app's event trigger fires and auto-approves the invite (governance + final approval)
- A Jira ticket is created in Jira project for audit tracking, then auto-transitioned to Done
- An hourly sweep catches any invites the event trigger missed
Public channel invites are skipped — they remain in the governance queue for manual approval.
Invites to pre-approved orgs (configured in your Slack admin settings) also flow through the event trigger, so they get tickets too — even though Slack auto-approves them.
Step 1: shared_channel_invite_requested
└─► connect_request_workflow
└─► ApproveConnectRequest (parse_connect_request.ts)
├── Filter: private channels only
├── Approve governance request
├── Verify invite cleared from queue
├── Create Jira ticket (Jira project)
├── Reply in thread with ticket link → #ea-slack-connect-requests
└── Post alert on failure → #ea-slack-connect-requests
Step 2: shared_channel_invite_accepted
└─► invite_accepted_workflow
└─► FinalizeConnectInvite (finalize_connect_invite.ts)
├── Detect inbound vs outbound
├── Inbound: Create Jira ticket + post notification
├── Outbound: Call approveSharedInvite to finalize connection
└── Post alert on failure
Safety net: hourly
└─► sweep_pending_workflow
└─► SweepPendingApprovals (sweep_pending_approvals.ts)
├── Check listConnectInvites for pending items
├── Check governance queue for stuck items
├── Approve any missed private-channel invites
├── Create Jira tickets for sweep approvals
└── Reply in thread with ticket link
manifest.ts # App manifest, scopes, outgoing domains
functions/
parse_connect_request.ts # Real-time approval logic + Jira + thread reply
sweep_pending_approvals.ts # Hourly sweep for missed approvals
finalize_connect_invite.ts # Final connection on invite acceptance + inbound tracking
jira_utils.ts # Jira ticket creation via JSM API
workflows/
connect_request_workflow.ts # Event-triggered workflow
invite_accepted_workflow.ts # Invite accepted workflow
sweep_pending_workflow.ts # Scheduled sweep workflow
triggers/
connect_request_message_trigger.ts # Event trigger (shared_channel_invite_requested)
invite_accepted_trigger.ts # Event trigger (shared_channel_invite_accepted)
sweep_pending_trigger.ts # Scheduled trigger (hourly)
- Slack CLI installed and authenticated
- A Slack workspace on a paid plan with Slack Connect enabled
- "Apply automation rules before channel invitations are sent" enabled in Slack Connect admin settings
- Jira access to a Jira Service Management project (for ticket creation)
| Account | Purpose |
|---|---|
svc-your-service-account@your-org.com |
Jira API authentication + ticket assignee |
app-owner@your-org.com |
Slack app owner (used for deploys) |
Before deploying, update config.ts with your org's values:
| Constant | Description |
|---|---|
ALERT_CHANNEL_ID |
Slack channel ID for notifications and alerts |
ENTERPRISE_ID |
Your Slack enterprise grid org ID (starts with E) |
PRIMARY_TEAM_ID |
Your primary workspace team ID (starts with T) |
BOT_APP_ID |
App ID of this bot after first deploy (starts with A) |
JIRA_BASE_URL |
Your Atlassian instance URL |
SERVICE_DESK_ID |
JSM service desk ID |
REQUEST_TYPE_ID |
Request type ID for Slack Connect tickets |
DONE_TRANSITION_ID |
Jira transition ID for "Done" status |
ASSIGNEE_ACCOUNT_ID |
Jira account ID of the service account assignee |
Set via slack env add (never commit these):
| Variable | Description |
|---|---|
JIRA_USER_EMAIL |
Jira service account email for API authentication |
JIRA_API_TOKEN |
Jira API token for the above account |
| Scope | Purpose |
|---|---|
channels:history |
Read public channel history (sweep) |
channels:read |
Read public channel metadata |
chat:write |
Post failure alerts |
conversations.connect:manage |
Approve/deny Slack Connect invites |
groups:history |
Read private channel history (sweep) |
groups:read |
Read private channel metadata |
users:read |
Look up user profiles (for Jira reporter) |
users:read.email |
Resolve user email addresses (for Jira raiseOnBehalfOf) |
# Deploy to Slack infrastructure
slack deploy
# Verify triggers survived the deploy
slack trigger list
# If the event trigger is missing, recreate it
slack trigger create --trigger-def triggers/connect_request_message_trigger.ts
# Delete the old/broken trigger if you had to recreate
slack trigger delete --trigger-id <old-trigger-id>Important:
slack deploycan break event triggers. Always verify withslack trigger listafter deploying and recreate if needed.
# Tail live activity logs
slack activity --tailKey log prefixes to watch:
| Prefix | Meaning |
|---|---|
[CONNECT] |
Real-time invite processing |
[APPROVE] |
Governance approval attempt |
[APPROVED] |
Governance approval succeeded |
[CONNECTED] |
Final invite approval succeeded |
[VERIFIED] |
Invite cleared from pending queue |
[SKIP] |
Public channel filtered out |
[SWEEP] |
Hourly sweep activity |
[JIRA] |
Jira ticket creation |
[INBOUND] |
Inbound invite tracking |
[ALERT] |
Failure alert posted to Slack |
Each approved invite creates a ticket in your configured Jira Service Management project:
Outbound invites (your user invites external person):
- Summary:
Slack Connect - <target email(s)> - Reporter: The user who sent the invite (via
raiseOnBehalfOf)
Inbound invites (external org invites your user):
- Summary:
Slack Connect Inbound - <recipient email> - Reporter: The user who accepted (via
raiseOnBehalfOf)
Common fields:
- Request type: Configured via
REQUEST_TYPE_IDin config.ts - Description: Full event metadata (actor, channel, target users, teams)
- Assignee: Service account configured via
ASSIGNEE_ACCOUNT_IDin config.ts - Workflow: Auto-transitioned to Done with resolution immediately
Jira failures are fully isolated - they are logged but never block invite approvals.
After each successful approval + ticket creation, the bot posts a rich standalone notification to the configured alert channel.
Outbound:
✅ Slack Connect Approved `ext-yourorg-partnerco` (private)
Invited by: Jane Smith -> partner@partnerco.com (Partner Co)
Ticket: PROJ-123
Inbound:
✅ Slack Connect Inbound (channel pending)
Accepted by: Alex Johnson
From: Sam Lee (Vendor Co)
Ticket: PROJ-124
Dedup: Before creating a ticket, the bot checks recent messages for an existing notification with the same target email. Prevents duplicate tickets when Slack fires multiple events for the same invite.
Failures and unverified approvals are posted to the configured alert channel. Check the Slack admin console if an alert fires.