User Token required — Bot Tokens cannot search. The Slack search.messages API endpoint is restricted to User Tokens (xoxp-...) with the search:read scope. Bot Tokens (xoxb-) will receive an not_allowed_token_type error regardless of scopes. User Tokens carry broad workspace access — store them with the same care as admin credentials and rotate them regularly.
When to Use
- Compliance audit: Search for messages containing specific keywords (PII references, policy violations, regulated terms) across the workspace for regulatory review or legal discovery workflows.
- Knowledge retrieval: Before starting a new initiative, search for previous discussions about the same topic across all accessible channels to surface relevant historical context and past decisions.
- Support triage: Search for customer-reported error messages, error codes, or product names to identify patterns across multiple support conversations and assess incident scope.
- Onboarding context: Collect historical channel discussions about a project or codebase component to give new team members background context without requiring them to scroll through months of history.
- Analytics and metrics: Count mentions of product names, feature flags, competitor names, or specific terms over a rolling time window for sentiment, adoption, or competitive intelligence reporting.
Configuration
Connection
| Field | Required | Description |
botToken | Required | User Token in xoxp-... format with search:read scope. Despite the field name botToken, this operation requires a User Token. Store in BizFirst Credentials Manager under a distinct credential name (e.g. slackUserToken) separate from your Bot Token. |
Operation
| Field | Required | Description |
query | Required | Full-text search query string. Supports Slack search modifiers: in:#channel-name, from:@username, before:YYYY-MM-DD, after:YYYY-MM-DD, during:month. Example: database error in:#engineering after:2024-01-01. |
sort | Optional | Sort field. Accepted values: score (default, relevance ranking) or timestamp (chronological order). |
sortDir | Optional | Sort direction: desc (default — newest or most relevant first) or asc. |
returnAll | Optional | Default true. When true, the node automatically paginates through all result pages and returns the complete match set. Set to false to return only a single page (use count and page for manual pagination). |
count | Optional | Results per page (1–100). Default 20. Only used when returnAll is false. |
page | Optional | Page number (1-based). Default 1. Only used when returnAll is false. |
Search modifier reference:
in:#channel-name — Restrict to a specific channel
from:@username — Messages from a specific user
before:2024-06-01 — Messages before a date (YYYY-MM-DD)
after:2024-01-01 — Messages after a date
during:2024-Q1 — Messages during a quarter (Q1–Q4)
"exact phrase" — Match an exact multi-word phrase
-word — Exclude messages containing a word
Sample Configuration
{
"resource": "message",
"operation": "search",
"botToken": "{{ $credentials.slackUserToken }}",
"query": "payment gateway error in:#engineering after:2024-05-01",
"sort": "timestamp",
"sortDir": "desc",
"returnAll": false,
"count": 50,
"page": 1
}
Validation Errors
| Error Code | Cause |
not_allowed_token_type | A Bot Token was used. Only User Tokens (xoxp-) can call search.messages. |
missing_scope | The User Token lacks the search:read OAuth scope. Re-authorize the app with the required scope. |
not_authed | The User Token is invalid, expired, or has been revoked. |
VAL_MISSING_QUERY | The query field is empty or not provided. |
Output
Success Port
Fires with the complete search results. When returnAll is true, all pages are fetched before this port fires.
| Field | Type | Description |
matchCount | integer | Number of matches returned in this response (may be less than totalCount when paginating). |
totalCount | integer | Total number of messages matching the query across all pages. |
pageCount | integer | Total number of pages available for the query. |
matches | array | Array of matched message objects. Each object contains: messageTs, channel (object with id and name), userId, text, permalink, previous (context), next (context). |
status | string | "ok" on success. |
errorCode | string | Empty on success. |
payload | object | Full raw Slack API response. |
Error Port
Fires when authentication fails, the token type is wrong, or the API call fails.
| Field | Type | Description |
status | string | "error" |
errorCode | string | Slack error code, e.g. not_allowed_token_type, missing_scope. |
payload | object | Full raw Slack error response. |
Sample Output
{
"matchCount": 3,
"totalCount": 3,
"pageCount": 1,
"matches": [
{
"messageTs": "1716644812.033100",
"channel": {
"id": "C04ENGINEERI",
"name": "engineering"
},
"userId": "U04DEV00001",
"text": "Getting a payment gateway error on checkout — `PaymentGatewayException: connection timeout`",
"permalink": "https://acmecorp.slack.com/archives/C04ENGINEERI/p1716644812033100"
},
{
"messageTs": "1716648100.091200",
"channel": {
"id": "C04ENGINEERI",
"name": "engineering"
},
"userId": "U04DEV00002",
"text": "Same payment gateway error here. Looks like Stripe's webhook endpoint is unreachable.",
"permalink": "https://acmecorp.slack.com/archives/C04ENGINEERI/p1716648100091200"
},
{
"messageTs": "1716652200.017400",
"channel": {
"id": "C04INCIDENTS",
"name": "incidents"
},
"userId": "U04OPS00001",
"text": "P2 incident opened for payment gateway errors — tracking in INC-4421",
"permalink": "https://acmecorp.slack.com/archives/C04INCIDENTS/p1716652200017400"
}
],
"status": "ok",
"errorCode": "",
"payload": {
"ok": true,
"messages": {
"total": 3,
"pagination": { "page": 1, "page_count": 1 },
"matches": []
}
}
}
Expression Reference
| Field | Expression | Notes |
| Total result count | {{ $output.searchMsgs.totalCount }} | Use to assess scope of a keyword across the workspace before processing individual matches. |
| Match count returned | {{ $output.searchMsgs.matchCount }} | Number of items in the matches array. Iterate over this many items in a downstream Loop node. |
| First match text | {{ $output.searchMsgs.matches[0].text }} | Text content of the first search result. |
| First match permalink | {{ $output.searchMsgs.matches[0].permalink }} | Direct URL to the first result. Embed in downstream notifications or database records. |
| First match channel name | {{ $output.searchMsgs.matches[0].channel.name }} | Human-readable channel name for the first result. |
| All matches array | {{ $output.searchMsgs.matches }} | Pass to a Loop node to process each result individually (extract text, log permalinks, etc.). |
Node Policies & GuardRails
| Policy Area | Recommendation |
| User Token security | User Tokens search as the authenticating user and can access all channels that user belongs to, including private channels. Store the token in BizFirst Credentials Manager under a distinct, clearly-named credential (e.g. slackUserTokenSearch). Do not share this credential across unrelated workflows. |
| Token rotation | Rotate User Tokens regularly. User Tokens do not expire by default, but should be invalidated and reissued if a team member who owns the token leaves the organization. |
| Compliance and privacy | Search results may surface private channel messages visible to the authenticated user. Treat search results as sensitive — do not log full message text to general-purpose logging systems. Write only the minimum required fields (timestamps, permalinks) to audit databases. |
| returnAll pagination | Setting returnAll: true on queries that match thousands of messages can result in many API calls and long execution times. Use targeted modifiers (in:#channel, date ranges) to narrow results before enabling returnAll. |
| Rate limits | search.messages is subject to Tier 2 rate limits (20+ req/min) and is also subject to workspace-level search rate limiting. Avoid calling this in high-frequency loops. |
| Query scoping | Use channel modifiers (in:#channel-name) to restrict searches to relevant channels. Broad unscoped queries return results from all channels the user can see, which may include channels outside the intended audit scope. |
| Error port handling | Always connect the error port. Token type errors (not_allowed_token_type) are common when the configuration accidentally uses a Bot Token instead of a User Token. |
Examples
Compliance Keyword Audit
{
"resource": "message",
"operation": "search",
"botToken": "{{ $credentials.slackUserToken }}",
"query": "\"social security\" OR \"SSN\" OR \"credit card number\" after:2024-01-01",
"sort": "timestamp",
"sortDir": "desc",
"returnAll": true
}
A monthly compliance workflow searches for messages that may contain regulated PII terms. The matches array is passed to a Loop node that writes each result's channel, user, timestamp, and permalink to a compliance audit database table. The total count is included in a compliance summary report posted to the #legal-compliance channel.
Scoped Channel Search for Support Triage
{
"resource": "message",
"operation": "search",
"botToken": "{{ $credentials.slackUserToken }}",
"query": "{{ $input.errorCode }} in:#customer-support after:{{ $input.windowStart }}",
"sort": "timestamp",
"sortDir": "asc",
"returnAll": false,
"count": 100,
"page": 1
}
When a new support ticket is opened referencing a specific error code, this search finds all recent #customer-support messages mentioning the same error. The results are analyzed to determine if multiple customers are hitting the same issue, and the count is used to set the incident priority level.
Knowledge Base Retrieval Before Kickoff
{
"resource": "message",
"operation": "search",
"botToken": "{{ $credentials.slackUserToken }}",
"query": "{{ $input.projectName }} in:#engineering in:#product during:{{ $input.lastQuarter }}",
"sort": "score",
"sortDir": "desc",
"returnAll": false,
"count": 20,
"page": 1
}
Before a project kickoff meeting, this workflow searches engineering and product channels for the last quarter's discussions about the project. The top 20 most relevant results (sorted by relevance score) are formatted and posted as a "context brief" to the project kick-off channel, saving team members from manually searching Slack history.