Skip to main content
Connect Slack to your agent or app. Corsair handles auth, data syncing, and webhook verification — you write the logic. What you get out of the box:
  • Send and receive messages
  • Query channels, users, files, and reactions — live or from local cache
  • Real-time webhook events (messages, joins, reactions, and more)
  • Automatic signature verification for every incoming event

Setup

1

Install

pnpm install @corsair-dev/slack
2

Add the plugin

corsair.ts
import { createCorsair } from 'corsair';
import { slack } from '@corsair-dev/slack';
import Database from 'better-sqlite3';

export const corsair = createCorsair({
    plugins: [slack()],
    database: new Database('corsair.db'),
    kek: process.env.CORSAIR_KEK!,
});
3

Get your credentials

You need a Slack bot token (xoxb-...) to make API calls, and a signing secret to verify incoming webhooks.See Get Credentials for step-by-step instructions on creating a Slack app and copying both values.
4

Store credentials

pnpm corsair setup --slack bot_token=xoxb-your-bot-token webhook_signature=your-signing-secret
Run this once. Credentials are encrypted with your KEK and stored in your database.

Authentication

The Slack plugin supports two auth methods:
The default. Create a Slack app, install it to your workspace, and copy the bot token.
corsair.ts
slack({
    authType: 'api_key',  // default, can be omitted
})
Store the token:
pnpm corsair setup --slack bot_token=xoxb-your-bot-token
See Get Credentials for where to find this in the Slack dashboard.

Send a message

await corsair.slack.api.messages.post({
    channel: 'C1234567890',  // channel ID
    text: 'Hello from Corsair!',
});
Use channel IDs, not names Slack’s API requires channel IDs (e.g. C1234567890), not names like #general. Query corsair.slack.api.channels.list() once to get the IDs, then cache them locally.

Query data

Corsair syncs Slack data to your local database. Query it instantly — no API call needed every time.
Direct Slack API calls — always fresh, always counts against rate limits:
// List channels
const channels = await corsair.slack.api.channels.list({
    exclude_archived: true,
});

// List users
const users = await corsair.slack.api.users.list();

// Post a message
await corsair.slack.api.messages.post({
    channel: 'C1234567890',
    text: 'Hello!',
});
Every API call automatically saves the response to your local database.

Receive webhooks

Set up a single webhook endpoint — Corsair routes and verifies everything:
app/api/webhook/route.ts
import { processWebhook } from 'corsair';
import { corsair } from '@/server/corsair';
import type { NextRequest } from 'next/server';
import { NextResponse } from 'next/server';

export async function POST(request: NextRequest) {
    const headers: Record<string, string> = {};
    request.headers.forEach((value, key) => { headers[key] = value; });

    const body = request.headers.get('content-type')?.includes('application/json')
        ? await request.json()
        : await request.text();

    const tenantId = new URL(request.url).searchParams.get('tenantId') ?? undefined;
    const result = await processWebhook(corsair, headers, body, { tenantId });

    if (!result.response) {
        return NextResponse.json({ success: false }, { status: 404 });
    }

    return NextResponse.json(result.response);
}
React to events with webhook hooks:
corsair.ts
slack({
    webhookHooks: {
        messages: {
            message: {
                after: async (ctx, result) => {
                    // Skip bot messages
                    if (result.data.bot_id) return;

                    console.log(`Message from ${result.data.user}: ${result.data.text}`);
                },
            },
        },
        users: {
            teamJoin: {
                after: async (ctx, result) => {
                    // Welcome new team members
                    await corsair.slack.api.messages.post({
                        channel: 'C_GENERAL_CHANNEL_ID',
                        text: `Welcome <@${result.data.user.id}>!`,
                    });
                },
            },
        },
    },
})
Available webhook events:
EventTrigger
messages.messageNew or updated message
channels.createdNew channel created
reactions.addedReaction added to message
users.teamJoinNew user joined team
users.userChangeUser profile updated
files.createdFile uploaded
challenge.challengeURL verification (handled automatically)
See Slack Webhooks for full payload shapes.

Plugin options

corsair.ts
slack({
    authType: 'api_key',        // 'api_key' | 'oauth_2'
    key: process.env.SLACK_BOT_TOKEN,         // optional, uses DB otherwise
    signingSecret: process.env.SLACK_SIGNING_SECRET, // optional, uses DB otherwise
    hooks: { /* endpoint hooks */ },
    webhookHooks: { /* webhook hooks */ },
    errorHandlers: { /* custom error handlers */ },
})
OptionTypeDescription
authType'api_key' | 'oauth_2'Authentication method (default: 'api_key')
keystringBot token — use DB storage instead for production
signingSecretstringWebhook signing secret — use DB storage instead for production
hooksobjectLogic that runs before/after API calls
webhookHooksobjectLogic that runs before/after webhook events
errorHandlersobjectCustom error handling per error type

Multi-tenancy

If you’re building a product where users connect their own Slack workspaces:
corsair.ts
export const corsair = createCorsair({
    multiTenancy: true,
    plugins: [slack()],
    // ...
});
usage.ts
const tenant = corsair.withTenant('user_abc123');

// All calls scoped to this user
await tenant.slack.api.messages.post({
    channel: 'C1234567890',
    text: 'Hello!',
});

const channels = await tenant.slack.db.channels.findAll();
Point each user’s Slack webhook to your endpoint with their ID:
https://your-app.com/api/webhook?tenantId=user_abc123
See Multi-Tenancy for the full guide.

What’s next

Get Credentials

Create a Slack app and copy your bot token and signing secret.

API Endpoints

All available Slack API operations and their parameters.

Webhooks

All Slack events, payload shapes, and verification details.

Database

Query synced Slack data from your local database.