> Part of the walkerOS documentation. Project overview and full index: <https://www.walkeros.io/llms.txt>

# Slack

<!-- -->

[Server](#)[ ](https://github.com/elbwalker/walkerOS/tree/main/packages/server/destinations/slack)

<!-- -->

[Source code](https://github.com/elbwalker/walkerOS/tree/main/packages/server/destinations/slack)[ ](https://www.npmjs.com/package/@walkeros/server-destination-slack)

<!-- -->

[Package](https://www.npmjs.com/package/@walkeros/server-destination-slack)

Send walkerOS events to Slack as formatted messages. Supports both Incoming Webhooks (simple, single-channel) and the Web API (multi-channel routing, threading, DMs, ephemeral messages, Block Kit).

<!-- -->

Where this fits

Slack is a **server destination** in the walkerOS flow:

Turns events into Slack messages for real-time alerts, sales notifications, moderation queues, or incident routing, without coupling your product code to Slack.

## Installation[​](#installation "Direct link to Installation")

```
npm install @walkeros/server-destination-slack
```

* Integrated
* Bundled

```
import { startFlow } from '@walkeros/collector';
import { destinationSlack } from '@walkeros/server-destination-slack';

await startFlow({
  destinations: {
    slack: {
      code: destinationSlack,
      config: {
        settings: {
          token: 'xoxb-your-bot-token',
          channel: '#notifications',
        },
      },
    },
  },
});
```

Add to your `flow.json` destinations:

```
"destinations": {
  "slack": {
    "package": "@walkeros/server-destination-slack",
    "import": "destinationSlack",
    "config": {
      "settings": {
        "token": "xoxb-your-bot-token",
        "channel": "#notifications"
      }
    }
  }
}
```

[See bundled mode setup](https://www.walkeros.io/docs/getting-started/modes/bundled.md) | [CLI reference](https://www.walkeros.io/docs/apps/cli.md)

## Configuration[​](#configuration "Direct link to Configuration")

This <!-- -->destination<!-- --> uses the standard <!-- -->destination<!-- --> config wrapper (consent, data, env, id, ...). For the shared fields see [destination<!-- --> configuration](https://www.walkeros.io/docs/destinations.md#configuration). Package-specific fields live under `config.settings` and are listed below.

## Settings[​](#settings "Direct link to Settings")

| Property        | Type                              | Description                                                                                                                                          | More |
| --------------- | --------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------- | ---- |
| `token`         | `string`                          | Slack Bot token (xoxb-...). Enables Web API mode. Mutually exclusive with webhookUrl.                                                                |      |
| `webhookUrl`    | `string`                          | Incoming Webhook URL. Enables webhook mode. Mutually exclusive with token.                                                                           |      |
| `channel`       | `string`                          | Default Slack channel ID or name (e.g. "#alerts" or "C024BE91L"). Required for Web API mode unless every rule supplies one. Ignored in webhook mode. |      |
| `text`          | `string`                          | Default text template. Supports \`${data.field}\` interpolation against the walkerOS event.                                                          |      |
| `blocks`        | `Array<object>`                   | Default Block Kit blocks applied when no mapping override is set.                                                                                    |      |
| `includeHeader` | `boolean`                         | Auto-add an event-name header block when generating default blocks. Default: true.                                                                   |      |
| `unfurlLinks`   | `boolean`                         | Enable link unfurling. Default: false (cleaner for automated alerts).                                                                                |      |
| `unfurlMedia`   | `boolean`                         | Enable media unfurling. Default: false.                                                                                                              |      |
| `mrkdwn`        | `boolean`                         | Use mrkdwn formatting in text. Default: true.                                                                                                        |      |
| `threadTs`      | `string`                          | Static thread\_ts for replies (rarely set at destination level).                                                                                     |      |
| `retryConfig`   | `'default' \| 'fiveRetriesInFive` | Retry policy passed to WebClient. Default: "default".                                                                                                |      |

## Mapping[​](#mapping "Direct link to Mapping")

Per-event rules under `config.mapping`. For the standard rule fields (consent, condition, data, batch, name, policy) see [mapping](https://www.walkeros.io/docs/mapping.md).

| Property         | Type            | Description                                                                                   | More |
| ---------------- | --------------- | --------------------------------------------------------------------------------------------- | ---- |
| `channel`        | `string`        | Override the destination channel for this rule. Web API mode only -- ignored in webhook mode. |      |
| `text`           | `string`        | Override the text template for this rule. Supports \`${data.field}\` interpolation.           |      |
| `blocks`         | `Array<object>` | Override Block Kit blocks for this rule.                                                      |      |
| `threadTs`       | `string`        | thread\_ts for posting as a reply in a thread.                                                |      |
| `replyBroadcast` | `boolean`       | Also broadcast the threaded reply back to the channel.                                        |      |
| `ephemeral`      | `boolean`       | Send via chat.postEphemeral. Requires \`user\`.                                               |      |
| `user`           | `string`        | Slack user ID for ephemeral or DM delivery.                                                   |      |
| `dm`             | `boolean`       | Send as DM via conversations.open + chat.postMessage. Requires \`user\`.                      |      |

## Examples

### Default blocks

With no custom text the destination auto-generates a Block Kit message from event data and source.

Event

```
{
  "name": "lead submit",
  "data": {
    "name": "Acme",
    "email": "sales@acme.test"
  },
  "context": {
    "dev": [
      "test",
      1
    ]
  },
  "globals": {
    "lang": "elb"
  },
  "custom": {
    "completely": "random"
  },
  "user": {
    "id": "us3r",
    "device": "c00k13",
    "session": "s3ss10n"
  },
  "nested": [
    {
      "entity": "child",
      "data": {
        "is": "subordinated"
      }
    }
  ],
  "consent": {
    "functional": true
  },
  "id": "0331458aac6ec18b",
  "trigger": "test",
  "entity": "lead",
  "action": "submit",
  "timestamp": 1700000600,
  "timing": 3.14,
  "source": {
    "count": 1,
    "trace": "0a1b2c3d4e5f60718293a4b5c6d7e8f9",
    "type": "crm",
    "platform": "server"
  }
}
```

Mapping

```
{
  "settings": {
    "channel": "#growth"
  }
}
```

Out

```
slackClient.chat.postMessage({
  "channel": "#growth",
  "text": "lead submit",
  "blocks": [
    {
      "type": "header",
      "text": {
        "type": "plain_text",
        "text": "lead submit"
      }
    },
    {
      "type": "section",
      "fields": [
        {
          "type": "mrkdwn",
          "text": "*name:*\nAcme"
        },
        {
          "type": "mrkdwn",
          "text": "*email:*\nsales@acme.test"
        }
      ]
    },
    {
      "type": "context",
      "elements": [
        {
          "type": "mrkdwn",
          "text": "Source: crm"
        }
      ]
    }
  ],
  "unfurl_links": false,
  "unfurl_media": false,
  "mrkdwn": true
})
```

### Webhook deploy

Without a token the destination posts to an incoming Slack webhook URL with the rendered message body.

Event

```
{
  "name": "deploy complete",
  "data": {
    "version": "1.4.2",
    "environment": "prod"
  },
  "context": {
    "dev": [
      "test",
      1
    ]
  },
  "globals": {
    "lang": "elb"
  },
  "custom": {
    "completely": "random"
  },
  "user": {
    "id": "us3r",
    "device": "c00k13",
    "session": "s3ss10n"
  },
  "nested": [
    {
      "entity": "child",
      "data": {
        "is": "subordinated"
      }
    }
  ],
  "consent": {
    "functional": true
  },
  "id": "177062690b49294d",
  "trigger": "test",
  "entity": "deploy",
  "action": "complete",
  "timestamp": 1700000700,
  "timing": 3.14,
  "source": {
    "count": 1,
    "trace": "0a1b2c3d4e5f60718293a4b5c6d7e8f9",
    "type": "collector",
    "schema": "4"
  }
}
```

Mapping

```
{
  "settings": {
    "text": ":rocket: Deployment complete: ${data.version} to ${data.environment}"
  }
}
```

Out

```
sendServer("https://hooks.slack.com/services/T00/B00/xxx", "{\"text\":\":rocket: Deployment complete: 1.4.2 to prod\",\"unfurl_links\":false,\"unfurl_media\":false,\"mrkdwn\":true}")
```

### Ephemeral message

A quota warning posts an ephemeral Slack message visible only to a target admin user.

Event

```
{
  "name": "quota warning",
  "data": {
    "remaining": 5
  },
  "context": {
    "dev": [
      "test",
      1
    ]
  },
  "globals": {
    "lang": "elb"
  },
  "custom": {
    "completely": "random"
  },
  "user": {
    "id": "us3r",
    "device": "c00k13",
    "session": "s3ss10n"
  },
  "nested": [
    {
      "entity": "child",
      "data": {
        "is": "subordinated"
      }
    }
  ],
  "consent": {
    "functional": true
  },
  "id": "476518bc4cdf0a70",
  "trigger": "test",
  "entity": "quota",
  "action": "warning",
  "timestamp": 1700000500,
  "timing": 3.14,
  "source": {
    "count": 1,
    "trace": "0a1b2c3d4e5f60718293a4b5c6d7e8f9",
    "type": "collector",
    "schema": "4"
  }
}
```

Mapping

```
{
  "settings": {
    "channel": "#admin",
    "ephemeral": true,
    "user": "U-ADMIN-1",
    "text": "Heads up: ${data.remaining} requests remaining"
  }
}
```

Out

```
slackClient.chat.postEphemeral({
  "channel": "#admin",
  "text": "Heads up: 5 requests remaining",
  "unfurl_links": false,
  "unfurl_media": false,
  "mrkdwn": true,
  "user": "U-ADMIN-1"
})
```

### Error alert

An error event posts a critical alert to an engineering channel using a mapping-level channel override.

Event

```
{
  "name": "error occur",
  "data": {
    "message": "Payment gateway timeout",
    "code": "PGW_TIMEOUT",
    "severity": "critical"
  },
  "context": {
    "dev": [
      "test",
      1
    ]
  },
  "globals": {
    "lang": "elb"
  },
  "custom": {
    "completely": "random"
  },
  "user": {
    "id": "us3r",
    "device": "c00k13",
    "session": "s3ss10n"
  },
  "nested": [
    {
      "entity": "child",
      "data": {
        "is": "subordinated"
      }
    }
  ],
  "consent": {
    "functional": true
  },
  "id": "72f8a07afdcab913",
  "trigger": "test",
  "entity": "error",
  "action": "occur",
  "timestamp": 1700000200,
  "timing": 3.14,
  "source": {
    "count": 1,
    "trace": "0a1b2c3d4e5f60718293a4b5c6d7e8f9",
    "type": "collector",
    "schema": "4"
  }
}
```

Mapping

```
{
  "settings": {
    "channel": "#engineering-alerts",
    "text": ":rotating_light: Error: ${data.message}"
  }
}
```

Out

```
slackClient.chat.postMessage({
  "channel": "#engineering-alerts",
  "text": ":rotating_light: Error: Payment gateway timeout",
  "unfurl_links": false,
  "unfurl_media": false,
  "mrkdwn": true
})
```

### Purchase alert

A completed order posts a templated message to a sales channel via the Slack Web API.

Event

```
{
  "name": "order complete",
  "data": {
    "id": "ORD-500",
    "total": 299.99,
    "currency": "EUR",
    "product": "Pro Plan"
  },
  "context": {
    "shopping": [
      "complete",
      0
    ]
  },
  "globals": {
    "pagegroup": "shop"
  },
  "custom": {
    "completely": "random"
  },
  "user": {
    "id": "buyer-42"
  },
  "nested": [
    {
      "entity": "product",
      "data": {
        "id": "ers",
        "name": "Everyday Ruck Snack",
        "color": "black",
        "size": "l",
        "price": 420
      },
      "context": {
        "shopping": [
          "complete",
          0
        ]
      },
      "nested": []
    },
    {
      "entity": "product",
      "data": {
        "id": "cc",
        "name": "Cool Cap",
        "size": "one size",
        "price": 42
      },
      "context": {
        "shopping": [
          "complete",
          0
        ]
      },
      "nested": []
    },
    {
      "entity": "gift",
      "data": {
        "name": "Surprise"
      },
      "context": {
        "shopping": [
          "complete",
          0
        ]
      },
      "nested": []
    }
  ],
  "consent": {
    "functional": true
  },
  "id": "167ffe6f3123cb14",
  "trigger": "load",
  "entity": "order",
  "action": "complete",
  "timestamp": 1700000100,
  "timing": 3.14,
  "source": {
    "count": 1,
    "trace": "0a1b2c3d4e5f60718293a4b5c6d7e8f9",
    "type": "collector",
    "schema": "4"
  }
}
```

Mapping

```
{
  "settings": {
    "channel": "#sales",
    "text": ":moneybag: New order: ${data.id} - ${data.total} ${data.currency}"
  }
}
```

Out

```
slackClient.chat.postMessage({
  "channel": "#sales",
  "text": ":moneybag: New order: ORD-500 - 299.99 EUR",
  "unfurl_links": false,
  "unfurl_media": false,
  "mrkdwn": true
})
```

### Threaded reply

A checkout step posts as a threaded reply in Slack via thread\_ts with broadcast to the channel.

Event

```
{
  "name": "checkout step",
  "data": {
    "step": "payment",
    "sessionTs": "1700000000.000050"
  },
  "context": {
    "dev": [
      "test",
      1
    ]
  },
  "globals": {
    "lang": "elb"
  },
  "custom": {
    "completely": "random"
  },
  "user": {
    "id": "us3r",
    "device": "c00k13",
    "session": "s3ss10n"
  },
  "nested": [
    {
      "entity": "child",
      "data": {
        "is": "subordinated"
      }
    }
  ],
  "consent": {
    "functional": true
  },
  "id": "69bdbdbd265178da",
  "trigger": "test",
  "entity": "checkout",
  "action": "step",
  "timestamp": 1700000400,
  "timing": 3.14,
  "source": {
    "count": 1,
    "trace": "0a1b2c3d4e5f60718293a4b5c6d7e8f9",
    "type": "collector",
    "schema": "4"
  }
}
```

Mapping

```
{
  "settings": {
    "channel": "#sales",
    "text": "Checkout step: ${data.step}",
    "threadTs": "1700000000.000050",
    "replyBroadcast": true
  }
}
```

Out

```
slackClient.chat.postMessage({
  "channel": "#sales",
  "text": "Checkout step: payment",
  "unfurl_links": false,
  "unfurl_media": false,
  "mrkdwn": true,
  "thread_ts": "1700000000.000050",
  "reply_broadcast": true
})
```

### Welcome DM

A user signup opens a Slack DM channel and posts a welcome message with the selected plan.

Event

```
{
  "name": "user signup",
  "data": {
    "plan": "enterprise"
  },
  "context": {
    "dev": [
      "test",
      1
    ]
  },
  "globals": {
    "lang": "elb"
  },
  "custom": {
    "completely": "random"
  },
  "user": {
    "id": "U-NEW-USER"
  },
  "nested": [
    {
      "entity": "child",
      "data": {
        "is": "subordinated"
      }
    }
  ],
  "consent": {
    "functional": true
  },
  "id": "d7d2a948a0a1d224",
  "trigger": "test",
  "entity": "user",
  "action": "signup",
  "timestamp": 1700000300,
  "timing": 3.14,
  "source": {
    "count": 1,
    "trace": "0a1b2c3d4e5f60718293a4b5c6d7e8f9",
    "type": "collector",
    "schema": "4"
  }
}
```

Mapping

```
{
  "settings": {
    "dm": true,
    "user": "U-NEW-USER",
    "text": ":wave: Welcome aboard! You signed up for the ${data.plan} plan."
  }
}
```

Out

```
slackClient.conversations.open({
  "users": "U-NEW-USER"
});

slackClient.chat.postMessage({
  "channel": "D-MOCK-DM",
  "text": ":wave: Welcome aboard! You signed up for the enterprise plan.",
  "unfurl_links": false,
  "unfurl_media": false,
  "mrkdwn": true
})
```

## Webhook vs Web API modes[​](#webhook-vs-web-api-modes "Direct link to Webhook vs Web API modes")

Pick exactly one auth method per destination. Each mode trades capability for simplicity.

| Capability         | Incoming Webhook (`webhookUrl`) | Web API (`token`)                              |
| ------------------ | ------------------------------- | ---------------------------------------------- |
| Setup              | Paste a URL, done               | Create a Slack app, install bot, manage scopes |
| Channels           | Fixed to the webhook's channel  | Any channel the bot is in, routable per rule   |
| Threading          | Not supported                   | `threadTs` + `replyBroadcast`                  |
| Direct messages    | Not supported                   | `dm: true` + `user`                            |
| Ephemeral messages | Not supported                   | `ephemeral: true`                              |
| Retries            | Plain HTTP                      | SDK-managed (`retryConfig`)                    |
| Block Kit          | Yes                             | Yes                                            |

Use **webhooks** for a single firehose channel and minimal setup. Use the **Web API** whenever you need routing, threading, DMs, or ephemeral messages.

## Channel routing[​](#channel-routing "Direct link to Channel routing")

With Web API mode, route events to different channels using nested `entity > action` mapping. Each rule can override `channel`, `text`, `blocks`, and threading behavior.

```
"destinations": {
  "slack": {
    "package": "@walkeros/server-destination-slack",
    "import": "destinationSlack",
    "config": {
      "settings": {
        "token": "$env.SLACK_BOT_TOKEN",
        "channel": "#events"
      },
      "mapping": {
        "order": {
          "complete": {
            "settings": {
              "channel": "#sales",
              "text": "New order ${data.id}: ${data.total} ${data.currency}"
            }
          }
        },
        "error": {
          "report": {
            "settings": {
              "channel": "#incidents",
              "threadTs": "data.incidentTs"
            }
          }
        },
        "user": {
          "signup": {
            "settings": {
              "channel": "#growth"
            }
          }
        }
      }
    }
  }
}
```

Events that don't match any rule fall back to the default `channel` in `settings`.

## Message formatting[​](#message-formatting "Direct link to Message formatting")

Messages support two formatting styles, set at destination level or per rule:

* **Text templates**: `${data.field}` placeholders interpolate against the walkerOS event. Good for terse alerts.
* **Block Kit**: structured layouts via Slack's [Block Kit](https://api.slack.com/block-kit) (headers, sections, buttons, context). Use the `blocks` setting when you need rich formatting.

When neither `text` nor `blocks` is provided, the destination generates a default message with an event-name header (toggle via `includeHeader`).
