channel/getReplies
Retrieves all reply messages in a Slack message thread, identified by the parent message's timestamp. The first message in the response is always the parent (thread root). Requires channels:history scope.
When to Use
- Incident thread collection: After an incident is resolved, collect all replies in the incident report thread to build a complete post-mortem timeline.
- Approval thread export: Pull the complete approval discussion thread for audit logging when a process requires documented sign-off conversations.
- Structured form response extraction: Read structured data from a threaded bot-driven form where each reply represents a step in a workflow response.
- AI thread summarization: Feed the full thread context — parent + all replies — to an LLM for a concise summary digest.
- Resolution time tracking: Calculate time from the first reply to the last reply to measure response SLA compliance in support channels.
Configuration
Connection
| Field | Type | Required | Description |
|---|---|---|---|
botToken | string | Required | Bot Token starting with xoxb-. Requires channels:history (public) or groups:history (private) scopes. |
Operation Fields
| Field | Type | Required | Description |
|---|---|---|---|
channelId | string | Required | The channel containing the parent message. Starts with C (public) or G (private). |
threadTs | string | Required | Timestamp of the parent (root) message. This is the thread identifier. Example: 1716729600.001200. |
returnAll | boolean | Optional | When true, paginates through all replies. Default: true. |
limit | integer | Optional | Max replies to return when returnAll is false. Default: 100. |
Thread Timestamp: The
threadTs is the timestamp of the first (parent) message in the thread — not a reply. You can find it in the messageTs field from channel/getHistory output, or from a Slack event payload's thread_ts field.
Sample Configuration
{
"operation": "channel/getReplies",
"connection": {
"botToken": "{{credentials.slackBotToken}}"
},
"fields": {
"channelId": "{{$json.channelId}}",
"threadTs": "{{$json.threadTs}}",
"returnAll": true
}
}
Validation Errors
| Error Code | Cause | Resolution |
|---|---|---|
channel_not_found | Invalid channel ID or bot not in private channel. | Verify the channel ID. Join the channel with channel/join if needed. |
thread_not_found | The threadTs does not correspond to a known thread root in this channel. | Verify the timestamp matches a parent message. Timestamps are exact — do not round or truncate them. |
missing_scope | Bot token lacks channels:history or groups:history scope. | Re-authorize the Slack app with history scopes. |
Output
Success Port
| Field | Type | Description |
|---|---|---|
messageCount | number | Total messages returned, including the parent message as the first item. |
messages | array | Array of message objects: messageTs, channel, userId, text, permalink. Index 0 is always the parent message. |
nextCursor | string | Pagination cursor. Empty when all replies have been returned. |
status | string | success or error. |
errorCode | string | Slack error code when status is error. |
payload | object | Full raw Slack API response. |
Error Port
On failure with Continue on Error enabled, routes to the error port with status: "error" and populated errorCode.
Sample Output
{
"messageCount": 3,
"messages": [
{
"messageTs": "1716729600.001200",
"channel": "C08INCIDENTS",
"userId": "U01ALERT99999",
"text": "INCIDENT P1: Database connection pool exhausted — prod affected",
"permalink": "https://yourworkspace.slack.com/archives/C08INCIDENTS/p1716729600001200"
},
{
"messageTs": "1716729720.001300",
"channel": "C08INCIDENTS",
"userId": "U01USER12345",
"text": "On it — investigating RDS connections now",
"permalink": "https://yourworkspace.slack.com/archives/C08INCIDENTS/p1716729720001300"
},
{
"messageTs": "1716730200.001400",
"channel": "C08INCIDENTS",
"userId": "U01USER12345",
"text": "Resolved — connection pool limit increased to 500, all services healthy",
"permalink": "https://yourworkspace.slack.com/archives/C08INCIDENTS/p1716730200001400"
}
],
"nextCursor": "",
"status": "success",
"errorCode": "",
"payload": { "ok": true, "messages": [ ... ], "has_more": false }
}
Expression Reference
| Expression | Result |
|---|---|
{{$node["channel-getreplies"].json.messageCount}} | Total messages including parent — subtract 1 for reply count only. |
{{$node["channel-getreplies"].json.messages[0].text}} | Parent (root) message text — the thread-starting message. |
{{$node["channel-getreplies"].json.messages.slice(1).map(m => m.text).join('\n')}} | All reply texts joined — use as AI summarization input (excludes parent). |
{{$node["channel-getreplies"].json.messages.at(-1).messageTs}} | Timestamp of the last reply — use to calculate resolution time. |
{{parseFloat($node["channel-getreplies"].json.messages.at(-1).messageTs) - parseFloat($json.threadTs)}} | Thread duration in seconds from first to last message. |
Node Policies & GuardRails
| Policy Area | Recommendation |
|---|---|
| Scope requirements | Requires channels:history for public channels and groups:history for private channels. Bot must be a channel member. |
| Rate limiting | Tier 3 (50+ req/min). Safe for individual thread fetches. For bulk thread processing, add delays between calls to avoid rate limit errors. |
| No hard-coded IDs | Always source channelId and threadTs from upstream event data or expressions. Never hard-code timestamps. |
| Timestamp precision | Slack message timestamps are float strings with 6 decimal places. Never round or truncate them — doing so will result in thread_not_found errors. |
| Parent message inclusion | The first element of the messages array is always the parent message. Use .slice(1) to isolate replies when you only need the response content. |
| Privacy compliance | Thread content is user-generated data. Ensure any downstream AI processing or storage complies with data retention and privacy policies. |
Examples
Example 1 — Incident Post-Mortem Thread Export
After an incident is resolved, collects the full thread and formats it for a post-mortem report document.
{
"operation": "channel/getReplies",
"fields": {
"channelId": "C08INCIDENTS",
"threadTs": "{{$json.incidentThreadTs}}",
"returnAll": true
}
}
// Code node: calculate resolution time
const messages = $node["channel-getreplies"].json.messages;
const durationSec = parseFloat(messages.at(-1).messageTs) - parseFloat(messages[0].messageTs);
const resolutionMinutes = Math.round(durationSec / 60);
return { messages, resolutionMinutes, replyCount: messages.length - 1 };
Example 2 — Approval Thread Audit Log
When a process approval completes, exports the approval discussion thread to the audit database.
// Triggered by approval-complete event
// $json.channelId = approval channel
// $json.approvalThreadTs = parent message timestamp
{
"operation": "channel/getReplies",
"fields": {
"channelId": "{{$json.channelId}}",
"threadTs": "{{$json.approvalThreadTs}}"
}
}
// HTTP Request → POST thread to audit API endpoint
Example 3 — AI Thread Summary Digest
Daily workflow that summarizes all active incident threads and posts digests to the ops channel.
// For each open incident thread:
{
"operation": "channel/getReplies",
"fields": { "channelId": "C08INCIDENTS", "threadTs": "{{$json.threadTs}}" }
}
// Format replies for LLM:
const replies = $node["channel-getreplies"].json.messages.slice(1)
.map(m => `${m.userId}: ${m.text}`).join('\n');
// AI node → summarize → message/send to #incidents-digest