Ephemeral vs DM: An ephemeral message appears in a channel but is only visible to one user — other channel members never see it. This is distinct from a DM, which creates a separate conversation. Use ephemeral messages for in-context feedback that does not need to be permanent (validation errors, help hints, moderation notices). Use DMs when the message needs to survive across sessions or be replied to.
When to Use
- Private validation feedback: Notify a user that their form submission had validation errors (e.g., missing required fields) without broadcasting the failure to the entire channel.
- Contextual help hints: Show targeted help text to a specific user after they invoke a slash command — guidance visible only to them, in the channel where they typed the command.
- Moderation notices: Warn a user privately that a message they posted violated channel guidelines, without calling them out publicly in front of other members.
- Onboarding prompts: Guide new channel members with orientation tips (pin location, channel purpose, conventions) shown only to them when they first join.
- Debug and diagnostic output: Show pipeline diagnostic information or verbose error details to an administrator in a channel without polluting the conversation for everyone else.
Configuration
Connection
| Field | Required | Description |
botToken | Required | Bot Token in xoxb-... format. Must have chat:write scope. Store in BizFirst Credentials Manager. |
Operation
| Field | Required | Description |
channel | Required | The channel where the ephemeral message will appear. The target user must be a member of this channel. Cannot be a DM channel ID — use message/send with a user ID for DMs. |
targetUserId | Required | Slack user ID (U...) of the user who will see the message. Only this user will see it. |
text | Required | Message text. Supports mrkdwn formatting. Keep the message clear and actionable since the user cannot reply to an ephemeral message to start a thread. |
threadTs | Optional | Show the ephemeral message in the context of a specific thread. Useful for providing in-thread guidance to a participant. |
blocksJson | Optional | Block Kit JSON for rich layout. Note that interactive components (buttons, select menus) within ephemeral messages have limited support and cannot initiate modal flows reliably. |
Sample Configuration
{
"resource": "message",
"operation": "sendEphemeral",
"botToken": "{{ $credentials.slackBotToken }}",
"channel": "C04GENERALCH",
"targetUserId": "{{ $input.userId }}",
"text": ":warning: Your expense report submission was incomplete. Missing fields: *{{ $input.missingFields }}*.\nPlease resubmit at <https://expenses.internal/submit|expenses.internal>."
}
Validation Errors
| Error Code | Cause |
channel_not_found | The specified channel does not exist or is not accessible to the bot. |
user_not_in_channel | The target user is not a member of the specified channel. Ephemeral messages can only be sent to channel members. |
not_in_channel | The bot itself is not a member of the specified channel. |
invalid_blocks | The blocksJson string is malformed. |
VAL_MISSING_CHANNEL | The channel field is empty or not provided. |
VAL_MISSING_TARGET_USER | The targetUserId field is empty or not provided. |
VAL_MISSING_TEXT | The text field is empty or not provided. |
Output
Success Port
Fires when Slack confirms the ephemeral message was delivered. Note that ephemeral messages do not have a persistent messageTs — they cannot be updated, deleted, or replied to.
| Field | Type | Description |
status | string | "ok" on success. |
errorCode | string | Empty on success. |
payload | object | Full raw Slack API response. The Slack API returns {"ok": true, "message_type": "ephemeral"} without a message timestamp. |
Error Port
Fires when the channel or user cannot be found, the bot lacks channel membership, or the API call fails.
| Field | Type | Description |
status | string | "error" |
errorCode | string | Slack error code, e.g. user_not_in_channel, channel_not_found. |
payload | object | Full raw Slack error response. |
Sample Output
{
"status": "ok",
"errorCode": "",
"payload": {
"ok": true,
"message_type": "ephemeral"
}
}
Expression Reference
| Field | Expression | Notes |
| Operation status | {{ $output.notifyUser.status }} | "ok" on success. Use in IfCondition to confirm delivery before continuing. |
| Error code | {{ $output.notifyUser.errorCode }} | Non-empty if delivery failed — log this for user experience monitoring. |
| Raw response | {{ $output.notifyUser.payload.ok }} | Boolean true on success. The ephemeral endpoint does not return a message timestamp. |
| Message type | {{ $output.notifyUser.payload.message_type }} | Always "ephemeral" on success — useful for conditional logic that handles both regular and ephemeral send nodes. |
Node Policies & GuardRails
| Policy Area | Recommendation |
| No history retention | Ephemeral messages do not persist. Do not use them for compliance-required notifications that must appear in audit logs. Use message/send to a private DM for persistent records. |
| Channel membership check | Both the bot and the target user must be in the channel. If either is not a member, the send will fail. Pre-validate membership with channel/getMembers in user-variable workflows. |
| No reply capability | Recipients cannot reply to ephemeral messages — they disappear on reload. If a user response is needed, follow up with a message/sendAndWait DM. |
| Rate limits | chat.postEphemeral is Tier 3 (50+ req/min). In loops processing many users, add a short delay to avoid burst throttling. |
| Sensitive information | While ephemeral messages are invisible to others in the channel, they are not end-to-end encrypted. Do not send passwords, API keys, or highly sensitive PII even in ephemeral messages. |
| Credential storage | Always reference the bot token via {{ $credentials.slackBotToken }}. Never hard-code xoxb- tokens in node configuration. |
Examples
Form Validation Error Feedback
{
"resource": "message",
"operation": "sendEphemeral",
"botToken": "{{ $credentials.slackBotToken }}",
"channel": "C04HRFORMSVC",
"targetUserId": "{{ $input.submitterUserId }}",
"text": ":x: Your time-off request could not be processed.\n\n*Validation errors:*\n• Start date must be a future date\n• Reason field is required\n\nPlease <https://forms.internal/time-off|resubmit the form> with the corrections."
}
After a form validation node detects errors in a time-off request, this ephemeral message is shown only to the submitter in the HR forms channel. No other HR team members see the error, preserving the submitter's privacy.
Admin Diagnostics in Shared Channel
{
"resource": "message",
"operation": "sendEphemeral",
"botToken": "{{ $credentials.slackBotToken }}",
"channel": "C04DEPLOYCHAN",
"targetUserId": "{{ $input.triggerUserId }}",
"text": ":mag: *Deploy Diagnostics (visible to you only)*\nBuild ID: `{{ $input.buildId }}`\nArtifact hash: `{{ $input.artifactHash }}`\nDeploy target: `{{ $input.targetEnv }}`\nLogs: <{{ $input.logUrl }}|View full logs>"
}
A deployment workflow posts verbose diagnostic details (build IDs, artifact hashes, log URLs) only to the engineer who triggered the deploy, in the shared #deploys channel. Other channel members see only the standard deploy notification posted via message/send.
Onboarding Welcome Tip
{
"resource": "message",
"operation": "sendEphemeral",
"botToken": "{{ $credentials.slackBotToken }}",
"channel": "C04GENERAL01",
"targetUserId": "{{ $input.newMemberUserId }}",
"text": ":wave: Welcome to *#general*, {{ $input.displayName }}!\n\n*Quick tips:*\n• Keep posts relevant to company-wide announcements\n• Use <#C04WATERCOOL|#water-cooler> for casual chat\n• Reach the admin team at <@U04ADMINTEAM>\n\nThis message is only visible to you."
}
An onboarding workflow triggers when a new member joins #general and shows them a contextual welcome tip. Because it is ephemeral, the channel is not cluttered with a welcome for each new member, and other members are not interrupted.