ChatReceive node type: chat-receive
A passive listener node that suspends workflow execution and waits for an incoming chat message matching the configured source criteria. Unlike the Chat node (which sends a message first), ChatReceive only listens — it is used to build chatbot workflows, command processors, and inbound message-driven automations.
When to Use
- Chatbot command processing: A workflow that serves as an internal chatbot loops through a ChatReceive node waiting for the next command. The
keyword_filterensures only messages beginning with a specific prefix (e.g.!ticket) trigger the workflow step. - Support ticket creation from chat: A support workflow suspends at ChatReceive waiting for a user to describe their issue via the chat interface. The received message text is extracted and used to create a ticket in the downstream ServiceDesk API node.
- Employee self-service via messaging: HR and IT self-service workflows listen for specific chat messages (e.g.
request laptop,reset password) from any employee. Thefrom_channelscopes the listener to the designated self-service channel. - Chat-triggered approval collection: After a Chat node sends a question, a ChatReceive node in a separate sub-workflow collects the asynchronous reply from a different user who was not the original target — useful for escalation patterns.
- Conversational data entry: A multi-step conversational workflow alternates between Chat (ask question) and ChatReceive (collect answer) nodes to gather structured data over a series of natural-language turns without rendering a form.
FirstResponse) to register the listener, then routes to waiting. The workflow is durably persisted. When an incoming message matching the filter criteria arrives on the BizFirst chat infrastructure, the resume endpoint injects the message data into inputData (via the _message_received flag and related keys) and the node resumes on success.
Configuration
| Field | Required | Description |
|---|---|---|
from_channel |
Optional* | Accept messages from this channel identifier only. Example: "support-channel" or "it-helpdesk". At least one of from_channel or from_user must be provided. Can also be supplied at runtime via inputData["from_channel"] — runtime values take priority over config. |
from_user |
Optional* | Accept messages from this specific user only. Format: user:email@company.com. At least one of from_channel or from_user must be provided. Can also be supplied at runtime via inputData["from_user"]. |
keyword_filter |
Optional | Only accept messages whose text contains this keyword (case-insensitive substring match). Messages that do not contain the keyword are ignored and the listener remains active. Example: "!ticket" or "CONFIRM". The matched keyword is echoed in the output as keyword_matched. |
timeout_minutes |
Optional | Integer minutes before the waiting session expires. Set to 0 or omit for unlimited wait. When the session expires the EngageSession records the timeout and the workflow must be resumed manually or via an escalation mechanism. Must be >= 0. |
from_channel, from_user, and keyword_filter can all be supplied dynamically via inputData keys at runtime. Runtime values take precedence over static config values. This allows upstream nodes to dynamically target different channels or users within the same workflow.
Sample Configuration
Channel listener with keyword filter
{
"from_channel": "support-helpdesk",
"keyword_filter": "!ticket",
"timeout_minutes": 480
}
Listens on the support-helpdesk channel for any message containing !ticket. Messages that do not contain that keyword are ignored. After 8 hours with no matching message, the EngageSession expires.
User-specific listener (no keyword filter)
{
"from_user": "user:john.doe@company.com",
"timeout_minutes": 60
}
Accepts the next message from the specified user regardless of content. Useful in a back-and-forth conversational workflow immediately after a Chat node has sent a question to that same user.
Validation Errors
| Error Message | Cause & Fix |
|---|---|
Either from_channel or from_user is required for chat-receive node |
Both from_channel and from_user are absent from both the configuration and the runtime inputData. Set at least one before the node executes. This error routes to the error port. |
| Error port fires on Phase 1 (EngageSession failure) | The EngageSession creation failed at the infrastructure level. The node is designed to continue with suspension even if session creation fails (the error is logged as a warning), so the listener may still be registered without a resume token. Check ProcessEngage service health. |
Output
Waiting Port (waiting)
Fired during Phase 1. The workflow is suspended and the listener is registered in the EngageSession. Output is an empty map — there is no message data yet.
| Field | Type | Description |
|---|---|---|
| (empty) | — | The waiting port carries no output data. The EngageSession metadata (channel, user, keyword) is stored in the session Metadata field for the resume endpoint to use when routing the incoming message. |
Success Port (success)
Fired during Phase 2 when an incoming message matching all filter criteria is received. All message fields are available in the output.
| Field | Type | Description |
|---|---|---|
message | string | Full text of the received message (from inputData["_message_text"]). |
from_user | string | Identifier of the user who sent the message (from inputData["_message_from"]). |
channel | string | Channel the message arrived on (from inputData["_message_channel"]). |
received_at | string | ISO 8601 timestamp of message receipt (from inputData["_received_at"], or DateTime.UtcNow if absent). |
keyword_matched | string | The keyword that triggered acceptance (from inputData["_keyword_matched"], or the keyword_filter config value). Empty string when no keyword filter was configured. |
Error Port (error)
Fires during Phase 1 if neither from_channel nor from_user can be resolved from config or runtime inputData.
| Field | Type | Description |
|---|---|---|
error | string | "Either from_channel or from_user is required for chat-receive node" |
Sample Output
// Phase 1 — Waiting port
{}
// Phase 2 — Success port
{
"message": "!ticket create - Printer not working on floor 3, tray 2 is jammed",
"from_user": "user:bob.johnson@company.com",
"channel": "support-helpdesk",
"received_at": "2026-05-26T09:15:44Z",
"keyword_matched": "!ticket"
}
// Error port
{
"error": "Either from_channel or from_user is required for chat-receive node"
}
Expression Reference
Reference received message fields in downstream nodes using the workflow node key:
| Expression | Value |
|---|---|
{{ $output.receiveNode.message }} | Full text of the received chat message |
{{ $output.receiveNode.from_user }} | User identifier of the message sender |
{{ $output.receiveNode.channel }} | Channel the message arrived on |
{{ $output.receiveNode.received_at }} | ISO 8601 timestamp of message receipt |
{{ $output.receiveNode.keyword_matched }} | Keyword that matched the filter (or empty string) |
receiveNode with the actual key assigned to this node in the workflow designer. Expression paths are case-sensitive and must match the field names exactly as shown.
Node Policies & GuardRails
- Message sender authentication: The BizFirst chat infrastructure verifies the sender's identity before injecting the message into the workflow resume path. Only messages from authenticated users in the BizFirst tenant can resume a suspended ChatReceive node.
- Keyword filter as a gate: The
keyword_filteracts as a pre-filter at the infrastructure level. Messages that do not match the keyword are not presented to the workflow — the listener remains open and the workflow continues waiting. This prevents spurious resumes from unrelated channel activity. - Input sanitization: The
messagefield in the output is the raw received text. Downstream nodes that use message content to query databases or call APIs must sanitize or validate the text before use. Never pass raw user input directly to a SQL query or shell command node. - Rate limiting: Implement rate limiting upstream (e.g. a Switch node checking a Redis counter) if this node is part of a chatbot loop that could receive high message volume. The ChatReceive node itself does not enforce per-user or per-channel rate limits.
- Audit all inbound messages: Connect the
successport to a DataMapping or logging node that writes the received message, sender, channel, and timestamp to an audit log table before processing. This provides a durable record of all messages that triggered workflow actions. - Session creation is best-effort: If
IEngageSessionServiceis not registered or the session creation call fails, the node logs a warning and continues to thewaitingport. This means the listener is registered without a durable resume token — in this degraded state the workflow will wait but may not be resumable via the standard resume endpoint. EnsureIEngageSessionServiceis registered in production. - Timeout with fallback notification: Always set
timeout_minutesin production. Wire theerrorport (or connect a timeout monitoring pattern via EventWait) to a notification node so operators know a listener has expired without receiving a matching message. - Dynamic channel targeting: The node reads
from_channel,from_user, andkeyword_filterfrom runtimeinputDatafirst, falling back to config. Use this to build dynamic listener patterns where an upstream node computes the target channel based on the workflow's data context.
Examples
Example 1 — IT Helpdesk Chatbot Command Processor
An always-on chatbot workflow loops indefinitely. It sits at a ChatReceive node waiting for any message prefixed with !ticket in the IT helpdesk channel. When a matching message arrives, the workflow parses the command text, creates a ServiceDesk ticket via HTTP Request node, sends a confirmation reply via Chat node, then loops back to the ChatReceive node.
{
"from_channel": "it-helpdesk",
"keyword_filter": "!ticket",
"timeout_minutes": 0
}
timeout_minutes: 0 means unlimited wait — correct for an always-on chatbot listener. The received message value (e.g. "!ticket create - Printer jammed floor 3") is passed to a downstream CodeExecute node that strips the command prefix and extracts the issue description. The Switch node downstream routes !ticket create, !ticket update, and !ticket close to their respective API handler branches.
Example 2 — Conversational Expense Entry (Chat + ChatReceive Pair)
A workflow collects expense claim data through a natural-language conversation. A Chat node asks "What is the expense amount?" and a ChatReceive node immediately after waits for the reply from the same user. This pattern repeats for each field (amount, category, date, receipt reference).
{
"from_user": "{{expenseClaimantUserId}}",
"timeout_minutes": 30
}
The from_user is dynamically resolved from the workflow context — set to the same user who received the Chat message in the preceding node. No keyword filter is applied since the next message from this user is always the expected answer. The 30-minute timeout prevents the conversation from hanging if the user walks away. On timeout, the error port connects to a Chat node that sends a timeout reminder and re-asks the question.
Example 3 — Employee Self-Service Password Reset via Chat
An HR/IT self-service workflow listens on the employee-selfservice channel for any message containing reset password. When triggered, the workflow identifies the requestor, validates their identity via a follow-up Chat challenge, and then calls the identity provider API to initiate the password reset flow.
{
"from_channel": "employee-selfservice",
"keyword_filter": "reset password",
"timeout_minutes": 0
}
After the ChatReceive node fires on success, the from_user output field identifies the requesting employee. A downstream IfCondition node checks whether the user's account is active and not locked. If active, a Chat node sends a verification challenge ("Reply with your employee ID to confirm identity"). A second ChatReceive node (with from_user set to the same user) collects the response. Only after successful identity verification does the workflow call the password reset API. The audit logging node at the end records the full exchange for compliance review.