Portal Community
Common pattern — live status boards: Post a status message once with message/send (capturing messageTs), then update it repeatedly with message/update as the underlying state changes. This gives channel members a single, always-current message to reference rather than a stream of repetitive updates.

When to Use

Configuration

Connection

FieldRequiredDescription
botTokenRequiredBot Token in xoxb-... format. Must have chat:write scope and must be the same token used to originally post the message. Store in BizFirst Credentials Manager.

Operation

FieldRequiredDescription
channelRequiredChannel ID containing the message to update. Must match the channel the message was originally posted to.
messageTsRequiredTimestamp of the message to update (e.g. 1716700412.087200). This is the messageTs returned by the original message/send node. Treat it as the message's unique ID.
textOptionalNew text content to replace the existing message text. Required if blocksJson is not provided.
blocksJsonOptionalNew Block Kit blocks JSON array string. When provided, completely replaces the existing blocks. Required if text is not provided. Pass an empty array [] to remove all blocks and fall back to plain text.
At least one of text or blocksJson is required. Providing neither will result in a VAL_MISSING_CONTENT validation error. If you provide both, blocksJson is rendered and text serves as the fallback/notification text.

Sample Configuration

{
  "resource": "message",
  "operation": "update",
  "botToken": "{{ $credentials.slackBotToken }}",
  "channel": "C04INCIDENTS",
  "messageTs": "{{ $output.postIncident.messageTs }}",
  "text": "RESOLVED: payments-api incident — Service fully restored at {{ $now }}",
  "blocksJson": "[{\"type\":\"header\",\"text\":{\"type\":\"plain_text\",\"text\":\":white_check_mark: Incident Resolved\"}},{\"type\":\"section\",\"fields\":[{\"type\":\"mrkdwn\",\"text\":\"*Service:*\\n`payments-api`\"},{\"type\":\"mrkdwn\",\"text\":\"*Status:*\\n:large_green_circle: Fully Restored\"},{\"type\":\"mrkdwn\",\"text\":\"*Resolved at:*\\n{{ $now }}\"},{\"type\":\"mrkdwn\",\"text\":\"*Duration:*\\n{{ $input.incidentDurationMinutes }} minutes\"}]}]"
}

Validation Errors

Error CodeCause
message_not_foundNo message exists with the given messageTs in the specified channel. The message may have been deleted, or the timestamp is incorrect.
cant_update_messageThe bot token is not the original sender of the message, or the message type cannot be edited (e.g. system messages, bot message subtypes with restrictions).
channel_not_foundThe specified channel does not exist or is not accessible to the bot token.
edit_window_closedWorkspace admin settings restrict message editing after a certain time window. The original message is too old to edit.
invalid_blocksThe blocksJson string is malformed or references unsupported block types.
VAL_MISSING_CHANNELThe channel field is empty or not provided.
VAL_MISSING_TSThe messageTs field is empty or not provided.
VAL_MISSING_CONTENTNeither text nor blocksJson was provided. At least one is required.

Output

Success Port

Fires after Slack confirms the message has been updated. The messageTs is unchanged — the timestamp uniquely identifies the message and does not change on edit.

FieldTypeDescription
channelstringChannel ID where the message was updated.
messageTsstringTimestamp of the updated message (same as the original — it does not change on edit).
statusstring"ok" on success.
errorCodestringEmpty on success.
payloadobjectFull raw Slack API response including the updated message object.

Error Port

Fires when the message cannot be found, the bot is not the original sender, the edit window has closed, or the API call fails.

FieldTypeDescription
statusstring"error"
errorCodestringSlack error code, e.g. message_not_found, cant_update_message.
payloadobjectFull raw Slack error response.

Sample Output

{
  "channel": "C04INCIDENTS",
  "messageTs": "1716700412.087200",
  "status": "ok",
  "errorCode": "",
  "payload": {
    "ok": true,
    "channel": "C04INCIDENTS",
    "ts": "1716700412.087200",
    "message": {
      "type": "message",
      "subtype": "bot_message",
      "text": "RESOLVED: payments-api incident — Service fully restored.",
      "ts": "1716700412.087200",
      "edited": {
        "user": "U0LAN0Z89",
        "ts": "1716710000.000000"
      }
    }
  }
}

Expression Reference

FieldExpressionNotes
Updated channel{{ $output.updateStatus.channel }}Resolved channel ID — confirm the update landed in the expected channel.
Message timestamp{{ $output.updateStatus.messageTs }}Same as the original ts. Use this for further updates or to fetch the permalink.
Operation status{{ $output.updateStatus.status }}"ok" on success. Check this in downstream IfCondition nodes.
Error code{{ $output.updateStatus.errorCode }}Non-empty if the update failed — log for monitoring.
Edit timestamp{{ $output.updateStatus.payload.message.edited.ts }}The Slack-assigned timestamp of when the edit occurred — useful for audit records.

Node Policies & GuardRails

Policy AreaRecommendation
Store the original messageTsAlways persist the messageTs returned by message/send in a workflow variable or database record. Without it, you cannot update or delete the message later. Use a VariableAssignment node immediately after the send.
Same token requirementThe bot token used for message/update must be the same token that sent the original message. Using a different token or a user token will result in cant_update_message.
Edit window policySome Slack workspace administrators enable a message edit window (e.g. only editable within 5 minutes). Design workflows that need to update messages to do so promptly after the original send. Check workspace settings if you encounter edit_window_closed errors.
Block replacement is completeWhen providing blocksJson, the new array completely replaces all existing blocks. You must re-provide all blocks you wish to keep, not just the changed ones. Build the full updated Block Kit payload in your workflow before calling this node.
Error port handlingConnect the error port — especially in incident management workflows where failing to update a status message could mislead the team about the current state.
Credential storageStore the Bot Token in BizFirst Credentials Manager. The same credential used for the original send should be referenced here.

Examples

Incident Status Update — "Resolved"

{
  "resource": "message",
  "operation": "update",
  "botToken": "{{ $credentials.slackBotToken }}",
  "channel": "C04INCIDENTS",
  "messageTs": "{{ $vars.incidentMessageTs }}",
  "text": "RESOLVED: payments-api — fully restored",
  "blocksJson": "[{\"type\":\"header\",\"text\":{\"type\":\"plain_text\",\"text\":\":white_check_mark: Incident Resolved\"}},{\"type\":\"section\",\"fields\":[{\"type\":\"mrkdwn\",\"text\":\"*Service:* `{{ $vars.serviceName }}`\"},{\"type\":\"mrkdwn\",\"text\":\"*Resolved at:* {{ $now }}\"}]}]"
}

Updates an incident card originally posted during an alert. $vars.incidentMessageTs was stored by a VariableAssignment node at incident creation time. The resolved card replaces the original alert card in-place in #incidents.

CI/CD Build Progress Tracker

{
  "resource": "message",
  "operation": "update",
  "botToken": "{{ $credentials.slackBotToken }}",
  "channel": "C04DEPLOYS01",
  "messageTs": "{{ $vars.buildMessageTs }}",
  "text": "Build #{{ $input.buildNumber }} — {{ $input.currentStage }}",
  "blocksJson": "[{\"type\":\"section\",\"text\":{\"type\":\"mrkdwn\",\"text\":\"*Build #{{ $input.buildNumber }}* — {{ $input.currentStage }}\\nCommit: `{{ $input.commitSha }}`\\nBranch: `{{ $input.branch }}`\"}},{\"type\":\"section\",\"fields\":[{\"type\":\"mrkdwn\",\"text\":\"*Test:* {{ $input.testStatus }}\"},{\"type\":\"mrkdwn\",\"text\":\"*Deploy:* {{ $input.deployStatus }}\"}]}]"
}

A CI/CD workflow posts one message per build using message/send and updates it at each stage transition using this node. Channel members see a single build card per deployment rather than a series of status messages.

Approval Card — Decision Recorded

{
  "resource": "message",
  "operation": "update",
  "botToken": "{{ $credentials.slackBotToken }}",
  "channel": "{{ $output.waitApproval.channel }}",
  "messageTs": "{{ $output.waitApproval.messageTs }}",
  "text": "PO #{{ $input.poNumber }} — {{ $output.waitApproval.replyText | upper }} by {{ $output.lookupApprover.displayName }}",
  "blocksJson": "[{\"type\":\"section\",\"text\":{\"type\":\"mrkdwn\",\"text\":\"*PO #{{ $input.poNumber }}* — :white_check_mark: *{{ $output.waitApproval.replyText | upper }}*\\nDecision by: {{ $output.lookupApprover.displayName }}\\nAt: {{ $now }}\"}}]"
}

After the message/sendAndWait node receives an approval decision, this update node edits the original approval request card to show the final decision. The card is updated from "Pending Approval" to the actual decision, providing a permanent record in the channel history.