# Convex Slack Sync Slack workspaces, channels, messages, and reactions into Convex reactively. Send messages, open modals, and handle slash commands from Convex functions. This is an upstream package. It has not been adapted or qualified for Bijection. - Package: convex-slack - Source: https://github.com/sholajegede/convex-slack - npm: https://www.npmjs.com/package/convex-slack - Directory: https://bijection.com/components/convex-slack Read the upstream README for setup, license, and compatibility before using it. ## Upstream package documentation Recorded 2026-10-23. This describes convex-slack; check the Bijection adaptation separately. convex-slack syncs Slack workspaces, channels, messages, reactions, and users into isolated Convex tables via the Slack Events API, enabling reactive queries that update as conversations happen. It handles the full OAuth v2 multi-workspace install flow, cryptographically verifies inbound webhooks, and exposes actions for messaging, Block Kit modals, slash commands, file uploads, and interactivity payloads. Token rotation is handled transparently without additional application code. ### Use cases - Building a Slack-integrated internal tool where channel messages need to be queryable and reactive in a React frontend without polling - Implementing a multi-workspace Slack app with per-team bot token management and OAuth install flow handled by the backend - Triggering Convex actions in response to slash commands or button clicks, with cryptographically verified payloads logged to queryable tables - Posting automated messages or opening Block Kit modals from Convex mutations or scheduled functions in response to business events - Syncing Slack user and channel data into Convex on demand to power search, filtering, or audit features in your application ### How it works Set up starts by registering the component in `convex.config.ts` via `app.use(slack)`, then instantiating the `Slack` client with your Slack app credentials. Three signed `httpAction` handlers (`eventsHandler`, `interactivityHandler`, `commandsHandler`) are mounted on your HTTP router and registered with the corresponding Slack platform endpoints. Every inbound request is verified using Slack's HMAC-SHA256 signing scheme with a 5-minute replay window before any data is written. Incoming events from the Slack Events API are processed and persisted into isolated tables: `messages`, `reactions`, `channels`, `users`, `interactions`, `commands`, `files`, and `webhookEvents`. The `webhookEvents` table provides idempotency via `checkAndRecordEvent`, preventing duplicate processing. Messages and reactions are kept current automatically as `message`, `message_changed`, `message_deleted`, `reaction_added`, and `reaction_removed` events arrive. Channel and user data is backfilled lazily on first access via calls like `listConversationsFromSlack` or `getUserInfo`. Outbound operations are available as methods on the `Slack` client instance and are called from Convex actions: `postMessage`, `postEphemeral`, `updateMessage`, `deleteMessage`, `openView`, `pushView`, `updateView`, `publishHomeView`, `uploadFile`, and `respondToUrl`. All outbound Web API calls include retry logic with exponential backoff and jitter, honoring Slack's `Retry-After` header on rate limit responses. The component's tables are schema-isolated and only accessible through the functions the component exposes, not through your app's schema directly. ### Upstream README ## What this does `convex-slack` gives your Convex app a live, queryable view of every Slack workspace it's installed into, plus a full set of actions for driving the platform — kept in sync by Slack's Events API and interactivity payloads: - **Multi-workspace OAuth install** — `getAuthorizationUrl` / `exchangeCode` implement the full OAuth v2 authorization-code flow, storing one bot token per `team_id` so a single deployment serves every workspace that installs your app. Token rotation (refresh tokens) is handled transparently: `resolveToken` refreshes and persists a rotated token automatically before it expires. - **Reactive messages, threads, and reactions** — `message`, `message_changed`, `message_deleted`, `reaction_added`, and `reaction_removed` events keep a `messages` and `reactions` table current, so `useQuery` in your React app re-renders as a conversation happens. - **Channels and users, synced on demand** — `conversations.list/info` and `users.list/info` calls (yours or this component's own) populate `channels` and `users`, plus membership/rename/archive events keep them current without a full resync. - **Full messaging** — `postMessage`, `postEphemeral`, `updateMessage`, `deleteMessage`, `scheduleMessage`/`deleteScheduledMessage`, and `addReaction`/`removeReaction`, all callable directly from a Convex action. - **Block Kit modals and App Home** — `openView`/`pushView`/`updateView` wrap `views.open`/`views.push`/`views.update` for interactive modals, and `publishHomeView` wraps `views.publish` for a per-user App Home tab. - **Slash commands and interactivity, verified and logged** — three signed `httpAction` handlers (`eventsHandler`, `interactivityHandler`, `commandsHandler`) verify every request against your signing secret before anything is recorded, and every button click, modal submission, shortcut invocation, and slash command is logged to `interactions` / `commands` for your own handlers to react to. - **Modern file uploads** — `uploadFile` drives the current `files.getUploadURLExternal` -> upload -> `files.completeUploadExternal` sequence (the old `files.upload` is deprecated and never used here). - **Resilient Web API calls** — every outbound call retries on `429`/`5xx` responses and network failures with exponential backoff and jitter, honoring Slack's `Retry-After` header (and the JSON-level `ratelimited` error some methods return). - **Cryptographically verified inbound requests** — every Events API, Interactivity, and Slash Command delivery is verified against Slack's `v0=HMAC-SHA256(signingSecret, "v0:{timestamp}:{body}")` scheme, with a 5-minute replay window, before it's trusted. This is a [Convex component](https://convex.dev/components): its `installations`, `channels`, `users`, `messages`, `reactions`, `interactions`, `commands`, `files`, `scheduledMessages`, and `webhookEvents` tables live in an isolated schema, not your app's schema, and are only reachable through the functions this component exposes. ## Table of Contents - [convex-slack](#convex-slack) - [What this does](#what-this-does) - [Table of Contents](#table-of-contents) - [Install](#install) - [Quick Start](#quick-start) - [1. Add the component](#1-add-the-component) - [2. Create your Slack app](#2-create-your-slack-app) - [3. Set environment variables](#3-set-environment-variables) - [4. Mount the HTTP handlers](#4-mount-the-http-handlers) - [5. Wire up the OAuth install flow](#5-wire-up-the-oauth-install-flow) - [6. Initialize the client](#6-initialize-the-client) - [Usage](#usage) - [Send a message](#send-a-message) - [Reply in a thread](#reply-in-a-thread) - [Open a modal from a slash command](#open-a-modal-from-a-slash-command) - [React to a button click](#react-to-a-button-click) - [Upload a file](#upload-a-file) - [Token rotation](#token-rotation) - [What's synced automatically vs. on demand](#whats-synced-automatically-vs-on-demand) - [Testing](#testing) - [License](#license)