Slack
Transports

Slack

Send messages via Slack Web API

The Slack transport is included in @betternotify/slack. It calls the Slack Web API via fetch — no external HTTP dependencies required (Node 22+ / Bun built-in).

It is a thin delivery adapter. Better-Notify handles rendering and validation upstream; slackTransport() posts the rendered message to chat.postMessage.

Install

npm install @betternotify/slack

Usage

import { slackTransport } from '@betternotify/slack';

const transport = slackTransport({
  token: process.env.SLACK_BOT_TOKEN!,
  defaultChannel: '#notifications',
});

Pass it to createClient via transportsByChannel:

import { createClient } from '@betternotify/core';
import { slackChannel, slackTransport } from '@betternotify/slack';

const notify = createClient({
  catalog,
  transportsByChannel: {
    slack: slackTransport({ token: process.env.SLACK_BOT_TOKEN! }),
  },
});

Standalone usage

The transport can be used directly without the full pipeline:

import { slackTransport } from '@betternotify/slack';

const transport = slackTransport({ token: process.env.SLACK_BOT_TOKEN! });

const result = await transport.send(
  { text: 'Deploy complete!', to: '#releases' },
  { route: 'manual', messageId: 'msg-1', attempt: 1 },
);

console.log(result.data.ts); // Slack message timestamp

Options

Prop

Type

Block Kit

Use the blocks slot to send rich messages with Block Kit:

const catalog = rpc.catalog({
  deploy: rpc
    .slack()
    .input(z.object({ service: z.string(), version: z.string() }))
    .text(({ input }) => `${input.service} deployed ${input.version}`)
    .blocks(({ input }) => [
      { type: 'header', text: { type: 'plain_text', text: `Deploy: ${input.service}` } },
      {
        type: 'section',
        text: { type: 'mrkdwn', text: `Version *${input.version}* is now live.` },
      },
    ]),
});

The text field is always required — Slack uses it as the notification preview and fallback for clients that don't render blocks.

Threading

Reply in a thread by passing threadTs:

await notify.deploy.send({
  to: '#releases',
  threadTs: '1234567890.123456',
  input: { service: 'api', version: 'v2.1.0' },
});

Verifying the bot token

Call verify() at startup to confirm the token is valid:

const transport = slackTransport({ token: process.env.SLACK_BOT_TOKEN! });
const { ok, details } = await transport.verify!();

if (!ok) {
  throw new Error('Invalid Slack bot token');
}

console.log('Bot:', details); // { url, team, user, ... }

Error handling

Slack API errors throw NotifyRpcError with a mapped error code:

Slack errorCodeMeaning
invalid_auth, token_revoked, not_authed, account_inactive, missing_scopeCONFIGToken/auth issue
channel_not_found, no_text, invalid_blocks, invalid_arguments, msg_too_longVALIDATIONBad request data
ratelimited, othersPROVIDERServer/rate limit issue
import { NotifyRpcError } from '@betternotify/core';

try {
  await notify.alert.send({ to: '#ops', input });
} catch (err) {
  if (err instanceof NotifyRpcError && err.code === 'CONFIG') {
    console.error('Check your SLACK_BOT_TOKEN');
  }
}

On this page