Skip to content

feat(slack): native OAuth trigger#5323

Open
TheodoreSpeaks wants to merge 8 commits into
stagingfrom
improve/slack
Open

feat(slack): native OAuth trigger#5323
TheodoreSpeaks wants to merge 8 commits into
stagingfrom
improve/slack

Conversation

@TheodoreSpeaks

Copy link
Copy Markdown
Collaborator

Summary

  • Native OAuth Slack trigger — connect the official Sim app and trigger workflows from Slack events (DM, @mention, public/private channel message, reactions), routed by team_id; no per-workflow app or signing secret
  • Unified trigger with App Type toggle (Sim / Custom bring-your-own-app)
  • Channel filter with picker + advanced channel-ID input; event-type + bot-message filtering
  • Enables the assistant-agent tools via assistant:write, plus app_mentions:read / im:history scopes
  • Schema: nullable webhook.path + routing_key column (migration 0253, CONCURRENTLY index) + shared /api/webhooks/slack ingest route

Type of Change

  • New feature

Testing

Tested manually on dev — DMs and private-channel messages trigger workflows end-to-end (verified via CloudWatch). lint:check, check:api-validation:strict, and check:migrations origin/staging all pass.

Checklist

  • Code follows project style guidelines
  • Self-reviewed my changes
  • Tests added/updated and passing
  • No new warnings introduced
  • I confirm that I have read and agree to the terms outlined in the Contributor License Agreement (CLA)

TheodoreSpeaks and others added 4 commits June 30, 2026 19:59
Add assistant:write, app_mentions:read, and im:history to the Slack bot
OAuth scopes so the Set Assistant Status / Title / Suggested Prompts tools
(assistant.threads.*) work with users' existing Slack credentials — no new
app or credentials required. Restore the action_assistant trigger capability
(scope assistant:write) in the manifest generator.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WpeT8J5yVCrrNQB9Hzm9uS
@vercel

vercel Bot commented Jul 1, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
docs Skipped Skipped Jul 1, 2026 6:45pm

Request Review

@cursor

cursor Bot commented Jul 1, 2026

Copy link
Copy Markdown

PR Summary

High Risk
New shared webhook ingress, auth/signature verification, and routing-key fan-out are security- and reliability-sensitive; deploy-time token use and credential teardown must stay correct to avoid cross-workspace misrouting or stale triggers.

Overview
Introduces a native Slack OAuth trigger (slack_oauth) so workflows can listen on the official Sim app without a per-workflow URL or signing secret. The Slack block’s default trigger switches from slack_webhook to slack_oauth; App Type chooses Sim (OAuth account, channel picker / manual IDs, event and bot filters) or Custom (existing BYO webhook path + secrets).

Sim mode registers webhooks as slack_app with a nullable path and a routing_key (Slack team_id from auth.test at deploy). A new POST /api/webhooks/slack endpoint verifies SLACK_SIGNING_SECRET, maps Events API payloads to configured operations, applies channel/bot filters, and fans out to all matching workflows via findWebhooksByRoutingKey. Deploy sync skips path conflicts when there is no path; disconnecting OAuth deactivates slack_app webhooks for that credential.

OAuth scopes gain assistant:write, app_mentions:read, and im:history; the Slack provider resolves bot tokens from stored credentials for slack_app executions. Minor null-safe handling for nullable webhook paths elsewhere; docs preview drops biome index-key ignores.

Reviewed by Cursor Bugbot for commit 310061d. Bugbot is set up for automated code reviews on this repo. Configure here.

Comment thread apps/sim/app/api/webhooks/slack/route.ts
@greptile-apps

greptile-apps Bot commented Jul 1, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR adds native Slack OAuth triggers through the official Sim Slack app. The main changes are:

  • Shared Slack event ingest routed by Slack team_id.
  • Unified Slack trigger with Sim and custom app modes.
  • Slack event, channel, bot-message, reaction, and file handling updates.
  • Nullable webhook paths plus routing-key storage for shared app webhooks.

Confidence Score: 4/5

This is close, but the Slack channel filter should be fixed before merging.

  • Unknown and empty Slack event selections now fail closed.
  • Native Slack reaction and file formatting now has a credential-owner token path.
  • The channel filter can still run workflows for a stale manual channel ID.

apps/sim/app/api/webhooks/slack/route.ts

Important Files Changed

Filename Overview
apps/sim/app/api/webhooks/slack/route.ts Adds the shared Slack ingest route and event filtering, but the channel dispatch filter can still include stale manual channel IDs.
apps/sim/lib/webhooks/providers/slack.ts Adds shared Slack signature helpers, team lookup, channel extraction, and OAuth-token resolution for native Slack formatting.
apps/sim/lib/webhooks/deploy.ts Persists Slack native app webhooks with routing keys and custom Slack webhooks with per-workflow paths.

Reviews (4): Last reviewed commit: "fix(slack-trigger): empty operation sele..." | Re-trigger Greptile

Comment thread apps/sim/app/api/webhooks/slack/route.ts Outdated
},
}
}
try {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Reaction Text Token Missing

This branch uses the OAuth bot token only to fetch the Slack team_id, then stores a slack_app webhook without any token that slackHandler.formatInput() can read. Native reaction_added and reaction_removed triggers therefore skip fetchSlackMessageText() and deliver an empty event.text even though the connected Slack account has the needed scopes.

Comment on lines +106 to +116
id: 'botToken',
title: 'Bot Token',
type: 'short-input',
placeholder: 'xoxb-...',
description:
'The bot token from your Slack app. Required for downloading files attached to messages.',
password: true,
required: false,
mode: 'trigger',
condition: { field: 'appType', value: 'custom' },
},

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Native Files Never Download

The unified trigger only exposes botToken for appType: custom, and it removed the legacy includeFiles switch from the native Sim path. For native Slack OAuth message events with attachments, slackHandler.formatInput() sees hasFiles but has neither includeFiles === true nor a providerConfig.botToken, so workflows receive hasFiles: true with an empty files array and cannot process the uploaded file content.

# Conflicts:
#	apps/sim/blocks/blocks/slack.ts
#	apps/sim/lib/webhooks/deploy.ts
#	packages/db/migrations/meta/0253_snapshot.json
#	packages/db/migrations/meta/_journal.json
#	scripts/check-api-validation-contracts.ts
@TheodoreSpeaks

Copy link
Copy Markdown
Collaborator Author

@greptile review

@TheodoreSpeaks

Copy link
Copy Markdown
Collaborator Author

@greptile review — addressed the three findings: (1) unmapped/unselected events now drop instead of bypassing the Operations filter, (2) formatInput resolves the OAuth bot token for slack_app webhooks so reaction-message text works, (3) re-added the includeFiles toggle + token resolution so native file downloads work.

// Fire only for events that map to a selected Operation. Unmapped events
// (e.g. assistant_thread_*) and unselected events no-op — never bypass.
const selectedEvents = normalizeSelection(providerConfig.events)
if (selectedEvents.length > 0 && (!eventKey || !selectedEvents.includes(eventKey))) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Empty Selection Matches All
When the events multi-select is saved as an empty array, normalizeSelection(providerConfig.events) returns [] and this condition skips the operation filter entirely. An unmapped Slack event with eventKey === null can then fall through to queueWebhookExecution for every matching team_id. Empty selections should fail closed instead of acting like “match all”.

Comment thread apps/sim/lib/webhooks/providers/slack.ts Outdated
Comment thread apps/sim/app/api/webhooks/slack/route.ts
…token via credential owner not execution actor
@TheodoreSpeaks

Copy link
Copy Markdown
Collaborator Author

@greptile review — addressed both: empty operation selection now fires nothing (no match-all), and the OAuth token is resolved via the credential owner (not the execution actor) so it works in shared workspaces.

Comment thread apps/sim/app/api/webhooks/slack/route.ts
Comment on lines +155 to +158
...normalizeSelection(providerConfig.channelFilter),
...normalizeSelection(providerConfig.manualChannelFilter),
]
if (

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Stale Channel Matches The route merges the current channelFilter value with manualChannelFilter, so an old manual channel ID can keep matching after the user switches back to the picker. For example, if a workflow was once deployed with manual channel C123 and is later redeployed from the picker with only C456, the stale manualChannelFilter value can still be included here and events from C123 can run the workflow. Use one canonical channel selection for dispatch, or ignore inactive raw channel fields.

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes using default effort and found 2 potential issues.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 310061d. Configure here.

workflowId,
blockId: block.id,
provider,
triggerPath,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Redeploy leaves stale Slack routing

High Severity

After the first deploy, changing the linked Slack account, Operations, channel filters, or Sim/Custom app type can leave the existing slack_app webhook row unchanged. routingKey, provider, path, and providerConfig are only written on insert, while shouldRecreateExternalWebhookSubscription returns false for slack/slack_app, so events may route to the wrong workspace or not at all.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 310061d. Configure here.


if (isBot && providerConfig.filterBotMessages !== false) {
continue
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Custom Slack ignores Operations filter

Medium Severity

The unified slack_oauth trigger applies Operations, channel, and bot-message filtering only on /api/webhooks/slack for Sim app type. Custom app deliveries still use the path-based trigger route, which queues every verified Slack event without reading providerConfig.events or the channel filters.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 310061d. Configure here.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant