Portal Community

When to Use

Configuration

Connection

FieldRequiredDescription
botTokenRequiredBot Token in xoxb-... format. Must have chat:write scope. Store in BizFirst Credentials Manager — never inline.

Operation

FieldRequiredDescription
channelRequiredDestination: channel ID (C0123456789), channel name (#general), or user ID (U0123456789) for a DM. Channel IDs are preferred — names can break if the channel is renamed.
textRequiredMessage text. Supports mrkdwn: *bold*, _italic_, `code`, >blockquote, <URL|link text>, <@USERID> mentions. Used as fallback text when blocksJson is also provided.
threadTsOptionalPost as a threaded reply. Pass the ts timestamp of the parent message (e.g. 1716700000.123456). Omit to post a top-level message.
usernameOptionalOverride the bot's display name for this single message. Requires chat:write.customize scope.
iconEmojiOptionalOverride the bot's icon with an emoji for this message, e.g. :robot_face:. Requires chat:write.customize scope.
blocksJsonOptionalBlock Kit JSON array string for rich interactive layouts. When provided, Slack renders the blocks instead of the plain text. The text field still serves as the notification/fallback text. Validate at api.slack.com/tools/block-kit-builder before deploying.

Sample Configuration

{
  "resource": "message",
  "operation": "send",
  "botToken": "{{ $credentials.slackBotToken }}",
  "channel": "C04X9ABCDEF",
  "text": "*Alert:* Service `payments-api` is down in `us-east-1`. Severity: *P1*. <https://runbooks.internal/payments-api-down|View Runbook>",
  "username": "AlertBot",
  "iconEmoji": ":rotating_light:",
  "blocksJson": "[{\"type\":\"section\",\"text\":{\"type\":\"mrkdwn\",\"text\":\"*:rotating_light: P1 Incident*\\nService: `payments-api`\\nRegion: `us-east-1`\\nDetected: {{ $now }}\"}},{\"type\":\"actions\",\"elements\":[{\"type\":\"button\",\"text\":{\"type\":\"plain_text\",\"text\":\"View Runbook\"},\"url\":\"https://runbooks.internal/payments-api-down\",\"style\":\"danger\"}]}]"
}

Validation Errors

Error CodeCause
channel_not_foundThe specified channel ID or name does not exist or is not accessible to the bot token.
not_in_channelThe bot is not a member of the specified channel. Use channel/join first or invite the bot manually.
invalid_blocksThe blocksJson string is malformed or contains unsupported block types. Validate at api.slack.com/tools/block-kit-builder.
msg_too_longMessage text exceeds Slack's 40,000 character limit.
VAL_MISSING_CHANNELThe channel field is empty or not provided.
VAL_MISSING_TEXTThe text field is empty or not provided.

Output

Success Port

Fires after the message is confirmed delivered by Slack. The messageTs value is the unique timestamp/ID of the new message and is needed for update, delete, reply, and permalink operations.

FieldTypeDescription
messageTsstringSlack timestamp of the sent message (e.g. 1716700412.087200). Acts as the message ID — retain this for downstream update/delete/reply operations.
channelstringChannel ID where the message was posted (resolved to ID even if a name was provided).
userIdstringUser ID of the bot that sent the message.
textstringThe text content of the sent message as stored by Slack.
statusstring"ok" on success.
errorCodestringEmpty on success.
payloadobjectFull raw Slack API response for advanced downstream use.

Error Port

Fires when Slack rejects the request (invalid channel, permission error, malformed blocks, etc.) or when the network call fails. Always connect the error port to a logging or alerting node in production workflows.

FieldTypeDescription
statusstring"error"
errorCodestringSlack error code, e.g. channel_not_found, not_in_channel.
payloadobjectFull raw Slack error response.

Sample Output

{
  "messageTs": "1716700412.087200",
  "channel": "C04X9ABCDEF",
  "userId": "U0LAN0Z89",
  "text": "*Alert:* Service `payments-api` is down in `us-east-1`.",
  "status": "ok",
  "errorCode": "",
  "payload": {
    "ok": true,
    "channel": "C04X9ABCDEF",
    "ts": "1716700412.087200",
    "message": {
      "type": "message",
      "subtype": "bot_message",
      "text": "*Alert:* Service `payments-api` is down in `us-east-1`.",
      "ts": "1716700412.087200",
      "bot_id": "B0LAN0Z89"
    }
  }
}

Expression Reference

FieldExpressionNotes
Message timestamp{{ $output.sendAlert.messageTs }}Use this to update, delete, reply to, or get the permalink of this message in a later node.
Destination channel{{ $output.sendAlert.channel }}Resolved channel ID — use this when chaining to a message/update or message/delete node.
Operation status{{ $output.sendAlert.status }}"ok" on success. Use in an IfCondition node to branch on success/failure.
Error code{{ $output.sendAlert.errorCode }}Non-empty string if the error port fired. Log this for diagnostics.
Bot user ID{{ $output.sendAlert.userId }}The bot's Slack user ID. Useful for filtering the bot's own messages in channel/getHistory.
Full response{{ $output.sendAlert.payload.message.ts }}Access raw Slack API response fields when the top-level fields do not expose what you need.

Node Policies & GuardRails

Policy AreaRecommendation
Credential storageStore the Bot Token in BizFirst Credentials Manager and reference it via {{ $credentials.slackBotToken }}. Never hard-code the xoxb- string directly in node configuration.
Rate limitschat.postMessage is Tier 3: 50+ req/min with burst. In high-volume loops, introduce a Delay node (1 second) between sends to avoid Slack's per-channel 1 msg/sec soft limit and to prevent unintentional floods.
Thread hygieneUse threadTs for follow-up messages relating to the same event. Posting every update as a top-level message clutters the channel and makes tracking harder.
Block Kit validationValidate all blocksJson payloads at api.slack.com/tools/block-kit-builder before deploying to production. Invalid blocks silently fall back to text in some clients but cause hard errors in others.
PII and secretsNever post personally identifiable information, passwords, API keys, or internal infrastructure details in message text. Ephemeral messages (message/sendEphemeral) should be used for sensitive per-user feedback.
Error port handlingAlways connect the error port. A disconnected error port means channel access failures, bot permission errors, and rate limit responses are silently swallowed.
Channel targetingUse channel IDs rather than names in production. Channel names can be changed by workspace admins and will cause channel_not_found errors at runtime.

Examples

Incident Alert with Block Kit

{
  "resource": "message",
  "operation": "send",
  "botToken": "{{ $credentials.slackBotToken }}",
  "channel": "C04INCIDENTS",
  "text": "P1 Incident: payments-api is down in us-east-1",
  "iconEmoji": ":rotating_light:",
  "blocksJson": "[{\"type\":\"header\",\"text\":{\"type\":\"plain_text\",\"text\":\":rotating_light: P1 Incident\"}},{\"type\":\"section\",\"fields\":[{\"type\":\"mrkdwn\",\"text\":\"*Service:*\\n`{{ $input.serviceName }}`\"},{\"type\":\"mrkdwn\",\"text\":\"*Region:*\\n`{{ $input.region }}`\"},{\"type\":\"mrkdwn\",\"text\":\"*Severity:*\\n{{ $input.severity }}\"},{\"type\":\"mrkdwn\",\"text\":\"*Detected:*\\n{{ $now }}\"}]},{\"type\":\"actions\",\"elements\":[{\"type\":\"button\",\"text\":{\"type\":\"plain_text\",\"text\":\"View Runbook\"},\"url\":\"{{ $input.runbookUrl }}\",\"style\":\"danger\"}]}]"
}

Posts a structured incident card to #incidents with service details and a runbook button. The messageTs from the success port is stored and used by a later message/update node to change the card status to "Resolved" when the incident closes.

DM Order Confirmation

{
  "resource": "message",
  "operation": "send",
  "botToken": "{{ $credentials.slackBotToken }}",
  "channel": "{{ $input.customerSlackUserId }}",
  "text": "Your order #{{ $input.orderId }} has been confirmed. Estimated delivery: {{ $input.deliveryDate }}."
}

Sends a plain-text DM directly to a customer's Slack user ID. The user ID is resolved earlier in the workflow from the customer's profile in the CRM via the user/get node using their email address.

Threaded Follow-up Reply

{
  "resource": "message",
  "operation": "send",
  "botToken": "{{ $credentials.slackBotToken }}",
  "channel": "{{ $output.postDigest.channel }}",
  "text": "_Update at {{ $now }}:_ All 42 overnight jobs completed. Total records processed: *{{ $input.recordCount }}*.",
  "threadTs": "{{ $output.postDigest.messageTs }}"
}

Replies in the thread of a previously sent digest message. $output.postDigest.messageTs references the timestamp returned by an earlier message/send node named postDigest. This keeps all status updates inside a single thread rather than flooding the channel.