> For the complete documentation index, see [llms.txt](https://docs.cybus.io/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.cybus.io/guides/system-connectivity/analytics-monitoring-alerting/slack-integration.md).

# Slack Integration

This guide describes how to post shop floor notifications from Connectware to Slack channels. You configure a service commissioning file that posts machine alarms and production milestones to Slack through incoming webhooks. A complete example file is available at the end of this guide.

## Objectives

* Creating a Slack app with an incoming webhook per channel.
* Posting machine alarms as text messages, filtered by severity.
* Posting production milestones as structured Block Kit messages.

## Prerequisites

To follow this guide, you will need the following:

* A running instance of Cybus Connectware.
* A Slack workspace and permission to create Slack apps in it. Workspace owners can restrict who is allowed to install apps.
* Access to the [Admin UI](/access/admin-ui.md) with sufficient [user permissions](/access/user-management.md).
* Basic knowledge of MQTT and the Connectware [services](/data-flows/services.md) concept (for example, [service commissioning files](/data-flows/service-commissioning-files.md), [connections](/data-flows/service-commissioning-files/resources/cybus-connection.md), and [endpoints](/data-flows/service-commissioning-files/resources/cybus-endpoint.md)).

## Connectware and Slack Integration

Slack receives messages from external systems through [incoming webhooks](https://docs.slack.dev/messaging/sending-messages-using-incoming-webhooks): unique URLs of the form `https://hooks.slack.com/services/T00000000/B00000000/XXXXXXXXXXXXXXXXXXXXXXXX` that accept a JSON payload per HTTP POST request. The minimal payload is `{"text": "Hello, world."}`. Connectware sends these requests through the [HTTP/REST connector](/connectors/enterprise-connectors/http-rest.md).

Each webhook posts to exactly one channel, the one you select when you create it. To post to several channels, create one webhook per channel and one Connectware endpoint per webhook. This guide uses two channels: one for machine alarms and one for production milestones.

Slack [rate limits](https://docs.slack.dev/apis/web-api/rate-limits) incoming webhooks to one message per second per webhook, with short bursts allowed. Requests beyond the limit return HTTP status 429. Post curated notifications that people should read, not raw sensor streams. The `filter` rule in this guide keeps low-severity alarms out of the channel.

The MQTT topics in this guide follow an ISA-95-style equipment hierarchy (`<enterprise>/<site>/<area>/<line>/<cell>`). The mappings subscribe with wildcards across all levels, so any machine in the hierarchy is picked up without changing the integration. The topic levels appear in the Slack message, so the reader knows which machine raised the notification.

### Creating a Slack App with an Incoming Webhook

1. Open the [Slack app management page](https://api.slack.com/apps) and create a new app from scratch in your workspace.
2. In the app settings, select **Incoming Webhooks** and switch on **Activate Incoming Webhooks**.
3. Click **Add New Webhook to Workspace**, select the channel for machine alarms, and authorize the app.
4. Copy the webhook URL from the **Webhook URLs for Your Workspace** table.
5. Repeat the last two steps for the production milestones channel. Each webhook posts to one channel only.

### Slack Connection Properties

The webhook URL splits into two parts: the host `hooks.slack.com`, which is the same for every webhook, and the path starting with `/services/`, which identifies the app and the channel. We add the paths as parameters to the service commissioning file, so you can set them when you install the service. For the webhook URL `https://hooks.slack.com/services/T00000000/B00000000/XXXXXXXXXXXXXXXXXXXXXXXX`, the path is `/services/T00000000/B00000000/XXXXXXXXXXXXXXXXXXXXXXXX`.

Do not worry about copying the service commissioning file snippets together into one, the complete example file is available at the end of this guide.

* `alarmsWebhookPath`: The path of the webhook URL for the machine alarms channel.
* `milestonesWebhookPath`: The path of the webhook URL for the production milestones channel.
* `minimumSeverity`: The lowest alarm severity that is posted to Slack. Defaults to `3`.
* `topicRoot`: The root of the MQTT topic hierarchy. Defaults to `enterprise`.

{% hint style="danger" %}
The webhook path is a credential. Anyone who knows the full webhook URL can post messages to the channel, no further authentication is involved. Do not share webhook URLs and do not commit them to public version control. Slack actively searches for leaked webhook URLs and revokes them.
{% endhint %}

{% code lineNumbers="true" %}

```yaml
parameters:
  alarmsWebhookPath:
    description: Webhook path for the machine alarms channel, without the host
    type: string

  milestonesWebhookPath:
    description: Webhook path for the production milestones channel, without the host
    type: string

  minimumSeverity:
    description: Lowest alarm severity that is posted to Slack
    type: number
    default: 3

  topicRoot:
    description: Root of the MQTT topic hierarchy
    type: string
    default: enterprise
```

{% endcode %}

### Slack Connection

All webhooks share the host `hooks.slack.com`, so one `Cybus::Connection` resource serves both channels. The channel-specific webhook paths go into the endpoints.

{% code lineNumbers="true" %}

```yaml
resources:
  slackConnection:
    type: Cybus::Connection
    properties:
      protocol: Http
      connection:
        scheme: https
        host: hooks.slack.com
        port: 443
```

{% endcode %}

### Posting Machine Alarms

We define a write endpoint with the webhook path of the alarms channel and a mapping that feeds it from the MQTT topic hierarchy. Two rules process each alarm:

* The `filter` rule drops alarms with a `severity` below the `minimumSeverity` parameter, so the channel only shows alarms that people should react to.
* The `transform` rule builds the Slack payload. The HTTP/REST connector expects the request body in the `body` property of the message (see [Publishing Data to REST Servers](/connectors/enterprise-connectors/http-rest.md#publishing-data-to-rest-servers)). The [named wildcards](/data-flows/service-commissioning-files/resources/cybus-mapping.md#wildcards) of the topic make the equipment hierarchy levels available in `$context.vars`, so the machine identity ends up in the message without the machine having to send it.

{% code lineNumbers="true" %}

```yaml
alarmsEndpoint:
  type: Cybus::Endpoint
  properties:
    protocol: Http
    connection: !ref slackConnection
    write:
      path: !ref alarmsWebhookPath

alarmsMapping:
  type: Cybus::Mapping
  properties:
    mappings:
      - subscribe:
          topic: !sub '${topicRoot}/+site/+area/+line/+cell/alarms'
        publish:
          endpoint: !ref alarmsEndpoint
        rules:
          - filter:
              expression: !sub 'severity >= ${minimumSeverity}'
          - transform:
              expression: |
                {
                  "body": {
                    "text": "Alarm " & code & " on "
                      & $context.vars.site & "/" & $context.vars.line & "/" & $context.vars.cell
                      & ": " & message & " (severity " & $string(severity) & ")"
                  }
                }
```

{% endcode %}

A machine publishes its alarms as a JSON object, for example to `enterprise/hamburg/assembly/line-1/press-01/alarms`:

{% code lineNumbers="true" %}

```json
{ "code": "E-4711", "message": "Hydraulic pressure low", "severity": 4 }
```

{% endcode %}

This posts the message `Alarm E-4711 on hamburg/line-1/press-01: Hydraulic pressure low (severity 4)` to the alarms channel. An alarm with a `severity` of `2` is dropped by the filter and never reaches Slack. The `text` field supports Slack's [mrkdwn formatting](https://docs.slack.dev/messaging/formatting-message-text), for example `*bold*` for bold text.

### Posting Production Milestones with Block Kit

For structured messages, Slack accepts a `blocks` array of [Block Kit](https://docs.slack.dev/reference/block-kit/blocks/section-block) layout blocks in the webhook payload. A section block carries a `text` object and an optional `fields` array of up to ten text objects, which Slack renders as two columns of side-by-side text. Text objects with `"type": "mrkdwn"` use Slack's mrkdwn markup. When `blocks` is present, the top-level `text` property serves as the fallback shown in notifications.

The milestones mapping posts one section block per milestone, with the equipment hierarchy levels from `$context.vars` as fields:

{% code lineNumbers="true" %}

```yaml
milestonesEndpoint:
  type: Cybus::Endpoint
  properties:
    protocol: Http
    connection: !ref slackConnection
    write:
      path: !ref milestonesWebhookPath

milestonesMapping:
  type: Cybus::Mapping
  properties:
    mappings:
      - subscribe:
          topic: !sub '${topicRoot}/+site/+area/+line/+cell/milestones'
        publish:
          endpoint: !ref milestonesEndpoint
        rules:
          - transform:
              expression: |
                {
                  "body": {
                    "text": "Production milestone: " & milestone,
                    "blocks": [
                      {
                        "type": "section",
                        "text": {
                          "type": "mrkdwn",
                          "text": "*Production milestone:* " & milestone
                        },
                        "fields": [
                          { "type": "mrkdwn", "text": "*Site*\n" & $context.vars.site },
                          { "type": "mrkdwn", "text": "*Area*\n" & $context.vars.area },
                          { "type": "mrkdwn", "text": "*Line*\n" & $context.vars.line },
                          { "type": "mrkdwn", "text": "*Cell*\n" & $context.vars.cell }
                        ]
                      }
                    ]
                  }
                }
```

{% endcode %}

A milestone message published to `enterprise/hamburg/assembly/line-1/press-01/milestones` looks like this:

{% code lineNumbers="true" %}

```json
{ "milestone": "Order 4711 completed" }
```

{% endcode %}

## Verifying the Integration

1. Install the service and set the `alarmsWebhookPath` and `milestonesWebhookPath` parameters to the paths of your webhook URLs.
2. Check that the connection is in the **Connected** state on the service details page in the Admin UI. The host `hooks.slack.com` answers the connection probe without checking the webhook path, so the **Connected** state does not prove that the paths are valid.
3. Publish a test message with the payload `{ "code": "E-4711", "message": "Hydraulic pressure low", "severity": 4 }` to `enterprise/hamburg/assembly/line-1/press-01/alarms`, for example with an MQTT client or the Admin UI. The alarm appears in the alarms channel.
4. Publish the same message with `"severity": 2`. The filter drops it and nothing appears in Slack.
5. Publish a test message with the payload `{ "milestone": "Order 4711 completed" }` to `enterprise/hamburg/assembly/line-1/press-01/milestones`. The milestone appears in the milestones channel as a formatted message with the equipment hierarchy in two columns.
6. The result of every HTTP request is published to the `/res` topic of the endpoint. Use the [Data Explorer](/monitoring/data-explorer.md) to inspect it. A successful post returns the plain text body `ok`. If Slack rejects a request, for example because the webhook path is wrong or the webhook has been revoked, the message on the `/res` topic contains an `error` property with the HTTP status.

## Service Commissioning File Example

{% file src="/files/OE7X2DPOAy4efpQ7V76R" %}

{% code title="slack-example.yml" lineNumbers="true" expandable="true" %}

```yaml
---
# ----------------------------------------------------------------------------
# Service Commissioning File
# ----------------------------------------------------------------------------
# Copyright: Cybus GmbH
# Contact: support@cybus.io
# ----------------------------------------------------------------------------
# Slack Integration (Example)
# ----------------------------------------------------------------------------

description: >
  Service commissioning file for posting shop floor notifications from
  Connectware to Slack channels via incoming webhooks (Example)

metadata:
  name: Slack Integration
  provider: cybus
  homepage: https://www.cybus.io
  version: 1.0.0

parameters:
  alarmsWebhookPath:
    description: Webhook path for the machine alarms channel, without the host
    type: string

  milestonesWebhookPath:
    description: Webhook path for the production milestones channel, without the host
    type: string

  minimumSeverity:
    description: Lowest alarm severity that is posted to Slack
    type: number
    default: 3

  topicRoot:
    description: Root of the MQTT topic hierarchy
    type: string
    default: enterprise

resources:
  # Connection to the Slack webhook host, shared by all webhooks
  slackConnection:
    type: Cybus::Connection
    properties:
      protocol: Http
      connection:
        scheme: https
        host: hooks.slack.com
        port: 443

  # Posts machine alarms to the alarms channel
  alarmsEndpoint:
    type: Cybus::Endpoint
    properties:
      protocol: Http
      connection: !ref slackConnection
      write:
        path: !ref alarmsWebhookPath

  alarmsMapping:
    type: Cybus::Mapping
    properties:
      mappings:
        - subscribe:
            topic: !sub '${topicRoot}/+site/+area/+line/+cell/alarms'
          publish:
            endpoint: !ref alarmsEndpoint
          rules:
            # Only alarms at or above the minimum severity reach Slack
            - filter:
                expression: !sub 'severity >= ${minimumSeverity}'
            - transform:
                expression: |
                  {
                    "body": {
                      "text": "Alarm " & code & " on "
                        & $context.vars.site & "/" & $context.vars.line & "/" & $context.vars.cell
                        & ": " & message & " (severity " & $string(severity) & ")"
                    }
                  }

  # Posts production milestones to the milestones channel as Block Kit messages
  milestonesEndpoint:
    type: Cybus::Endpoint
    properties:
      protocol: Http
      connection: !ref slackConnection
      write:
        path: !ref milestonesWebhookPath

  milestonesMapping:
    type: Cybus::Mapping
    properties:
      mappings:
        - subscribe:
            topic: !sub '${topicRoot}/+site/+area/+line/+cell/milestones'
          publish:
            endpoint: !ref milestonesEndpoint
          rules:
            # The top-level text is the notification fallback for the blocks
            - transform:
                expression: |
                  {
                    "body": {
                      "text": "Production milestone: " & milestone,
                      "blocks": [
                        {
                          "type": "section",
                          "text": {
                            "type": "mrkdwn",
                            "text": "*Production milestone:* " & milestone
                          },
                          "fields": [
                            { "type": "mrkdwn", "text": "*Site*\n" & $context.vars.site },
                            { "type": "mrkdwn", "text": "*Area*\n" & $context.vars.area },
                            { "type": "mrkdwn", "text": "*Line*\n" & $context.vars.line },
                            { "type": "mrkdwn", "text": "*Cell*\n" & $context.vars.cell }
                          ]
                        }
                      ]
                    }
                  }
```

{% endcode %}


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://docs.cybus.io/guides/system-connectivity/analytics-monitoring-alerting/slack-integration.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
