Skip to main content
Ask your AI

Slack

Server Source code Package

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​

npm install @walkeros/server-destination-slack
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',
        },
      },
    },
  },
});

Configuration​

This destination uses the standard destination config wrapper (consent, data, env, id, ...). For the shared fields see destination configuration. Package-specific fields live under config.settings and are listed below.

Settings​

PropertyTypeDescriptionMore
tokenstringSlack Bot token (xoxb-...). Enables Web API mode. Mutually exclusive with webhookUrl.
webhookUrlstringIncoming Webhook URL. Enables webhook mode. Mutually exclusive with token.
channelstringDefault Slack channel ID or name (e.g. "#alerts" or "C024BE91L"). Required for Web API mode unless every rule supplies one. Ignored in webhook mode.
textstringDefault text template. Supports `${data.field}` interpolation against the walkerOS event.
blocksArray<object>Default Block Kit blocks applied when no mapping override is set.
includeHeaderbooleanAuto-add an event-name header block when generating default blocks. Default: true.
unfurlLinksbooleanEnable link unfurling. Default: false (cleaner for automated alerts).
unfurlMediabooleanEnable media unfurling. Default: false.
mrkdwnbooleanUse mrkdwn formatting in text. Default: true.
threadTsstringStatic thread_ts for replies (rarely set at destination level).
retryConfigRetry policy passed to WebClient. Default: "default".

Mapping​

Per-event rules under config.mapping. For the standard rule fields (consent, condition, data, batch, name, policy) see mapping.

PropertyTypeDescriptionMore
channelstringOverride the destination channel for this rule. Web API mode only -- ignored in webhook mode.
textstringOverride the text template for this rule. Supports `${data.field}` interpolation.
blocksArray<object>Override Block Kit blocks for this rule.
threadTsstringthread_ts for posting as a reply in a thread.
replyBroadcastbooleanAlso broadcast the threaded reply back to the channel.
ephemeralbooleanSend via chat.postEphemeral. Requires `user`.
userstringSlack user ID for ephemeral or DM delivery.
dmbooleanSend 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": "6323df53b76a3e4e",
  "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": "ee3c3eb93f9f29a7",
  "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": "b4661f4fb739585e",
  "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": "266b5d4d24184a0e",
  "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": "1859567d2dbc0398",
  "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": "ab9d56e47de5cf53",
  "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": "4f1aa47b22d48b4f",
  "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​

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

CapabilityIncoming Webhook (webhookUrl)Web API (token)
SetupPaste a URL, doneCreate a Slack app, install bot, manage scopes
ChannelsFixed to the webhook's channelAny channel the bot is in, routable per rule
ThreadingNot supportedthreadTs + replyBroadcast
Direct messagesNot supporteddm: true + user
Ephemeral messagesNot supportedephemeral: true
RetriesPlain HTTPSDK-managed (retryConfig)
Block KitYesYes

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​

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​

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 (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).

💡 Need implementation support?
elbwalker offers hands-on support: setup review, measurement planning, destination mapping, and live troubleshooting. Book a 2-hour session (€399)