Skip to content

Push Alerts to Slack via RPC

1. Overview

ThinkLink (TKL) RPC can call external HTTP endpoints (see RPC Model §1.3.2 Type 4) and forward device alerts to Slack in real time. Two integration options are covered:

  • Incoming Webhook (recommended / simple): one fixed URL per channel; can only post to that channel
  • Bot Token + chat.postMessage (flexible): one token can post to multiple channels and supports richer features

2. Prerequisites

2.1 Option A: Create an Incoming Webhook

  1. Visit https://api.slack.com/apps and click Create New App → From scratch
  2. Select the target workspace; the app name can be anything
  3. In the left menu under Incoming Webhooks, toggle Activate Incoming Webhooks
  4. Click Add New Webhook to Workspace and pick the destination channel
  5. Copy the generated Webhook URL, which looks like:
    https://hooks.slack.com/services/T00000000/B00000000/XXXXXXXXXXXXXXXXXXXXXXXX

2.2 Option B: Create a Bot Token

  1. In the same app config page, go to OAuth & Permissions
  2. Under Scopes → Bot Token Scopes, add: chat:write, chat:write.public (if posting to channels the bot has not joined)
  3. Click Install to Workspace. After authorizing, you'll receive the Bot User OAuth Token, which looks like:
    xoxb-1234567890-1234567890123-xxxxxxxxxxxxxxxxxxxxxxxx
  4. Invite the bot into the target channel: /invite @YourBotName

2.3 Protect the Webhook URL / Bot Token (Required Reading)

⚠️ Security notice:

  • A leaked Incoming Webhook URL allows anyone to post to that channel
  • A Bot Token grants broader access — depending on scopes, it may allow reading channel messages, posting to multiple channels, or modifying the bot's own configuration

Follow these rules:

  • Do not hard-code Webhook URLs or Bot Tokens into scripts, screenshots, or public repositories (GitHub's secret scanner automatically detects leaked Slack tokens)
  • Recommended: Store URLs/tokens in the device's (or asset's) server_attrs and reference them via device.server_attrs.slack_webhook or .slack_token
  • If you discover a leak, immediately revoke the token / disable the webhook in the Slack App admin page, then regenerate
  • Grant least privilege scopes — only request chat:write; do not add chat:read or users:read unless required
  • Periodically audit the app's installation scope and permissions
  • Use different apps or webhooks for different business scenarios to limit blast radius

3. Calling Slack from an RPC (Option A: Webhook)

3.1 Store Credentials in server_attrs

json
{
    "slack_webhook": "https://hooks.slack.com/services/T00000000/B00000000/XXXXXXXXXXXXXXXXXXXXXXXX"
}

3.2 RPC Script: Block Kit Rich Message

javascript
function rpc_script({ device, params, alarms, logger }) {
    let url = device.server_attrs?.slack_webhook;
    if (!url) {
        logger.error("slack_webhook not configured", { eui: device.eui });
        return null;
    }

    let levelEmoji = {
        urgent: ":rotating_light:",
        high:   ":warning:",
        mid:    ":large_yellow_circle:",
        low:    ":information_source:"
    }[params.level] || ":bell:";

    return [
        {
            sleepTimeMs: 0,
            type: "axios",
            dnMsg: {
                method: "POST",
                url: url,
                headers: { "Content-Type": "application/json" },
                data: {
                    text: `${levelEmoji} Device Alert - ${device.name}`,   // fallback for notifications
                    blocks: [
                        {
                            type: "header",
                            text: { type: "plain_text", text: `${levelEmoji} Device Alert` }
                        },
                        {
                            type: "section",
                            fields: [
                                { type: "mrkdwn", text: `*Device:*\n${device.name}` },
                                { type: "mrkdwn", text: `*EUI:*\n\`${device.eui}\`` },
                                { type: "mrkdwn", text: `*Level:*\n${(params.level || "").toUpperCase()}` },
                                { type: "mrkdwn", text: `*Time:*\n${new Date().toISOString()}` }
                            ]
                        },
                        {
                            type: "section",
                            text: { type: "mrkdwn", text: `*Description:*\n${params.desc || "n/a"}` }
                        }
                    ]
                },
                timeout: 5000
            }
        }
    ];
}

4. Calling Slack from an RPC (Option B: Bot Token)

4.1 Store Credentials in server_attrs

json
{
    "slack_token":   "xoxb-1234567890-xxxxxxxxxxxxxxxxxxxxxxxx",
    "slack_channel": "C01234ABCDE"
}

💡 slack_channel can be a channel ID (recommended, more stable) or #channel-name. To find the channel ID, right-click the channel in the Slack client → "View channel details" → scroll to the bottom.

4.2 RPC Script

javascript
function rpc_script({ device, params, alarms, logger }) {
    let token   = device.server_attrs?.slack_token;
    let channel = device.server_attrs?.slack_channel;
    if (!token || !channel) {
        logger.error("slack credentials not configured", { eui: device.eui });
        return null;
    }

    return [
        {
            sleepTimeMs: 0,
            type: "axios",
            dnMsg: {
                method: "POST",
                url: "https://slack.com/api/chat.postMessage",
                headers: {
                    "Authorization": `Bearer ${token}`,
                    "Content-Type":  "application/json; charset=utf-8"
                },
                data: {
                    channel: channel,
                    text: `:warning: *${device.name}* alert: ${params.desc || ""}`,
                    mrkdwn: true
                },
                timeout: 5000
            }
        }
    ];
}

5. RPC Parameter Configuration

Attr NameTypeAliasDescription
levelstringAlert levellow / mid / high / urgent
descstringAlert descriptionShown in the message body

6. Wiring into the Alert Flow

See WeCom AN §5 for the trigger script example — just change method to the name of this RPC.

7. Troubleshooting

SymptomLikely Cause
Webhook returns invalid_payloadMalformed JSON; blocks structure invalid
Webhook returns no_service / 404Wrong webhook URL, or it has been revoked
chat.postMessage returns ok: false, error: not_in_channelBot not invited to the target channel (use /invite @bot)
chat.postMessage returns error: missing_scopeBot Token lacks required scopes; add chat:write and reinstall the app
Request timeoutTKL server cannot reach hooks.slack.com / slack.com
Rate limitSlack limits each channel to ~1 message per second on average

8. Notes

  • Prefer Block Kit (blocks) over the legacy attachments field — it renders consistently across all Slack clients
  • Do not put the Bot Token itself in messages (e.g. don't dump full request bodies or stack traces)
  • Enable Token Auto-rotation (OAuth v2) in workspace settings to reduce the impact of long-term token leakage
  • For critical alerts, configure multiple notification channels (e.g. Slack + email) so a single-channel outage does not silence alerts