Rocket.Chat message search
Rocket.Chat AI search finds workspace messages by meaning as well as by exact terms, using a Foundation4 pipeline that holds the workspace's messages. This page describes how the integration divides the work, how to set up the pipeline and API key in Foundation4, how the Rocket.Chat settings map to Foundation4 values, and the search requests that Rocket.Chat sends. Operators who run Foundation4 for a Rocket.Chat workspace and integrators who diagnose search results need this page. Rocket.Chat message indexing describes how messages reach the pipeline.
Reader experience
AI search adds results that match the meaning of a query to Rocket.Chat's workspace search. A reader can restrict a search with the search operators in: (rooms), from: (senders), after: and before: (dates). Rocket.Chat can also generate a written answer that cites the messages found.
The behavior depends on the Rocket.Chat version:
- Rocket.Chat 8.7 and 8.8. AI search runs a similarity search only. AI search first shipped in Rocket.Chat 8.7.
- Rocket.Chat 8.9. AI search runs hybrid search: a similarity search and a full-text search, combined by rank in Rocket.Chat. Two settings control the combination: Search balance and Recency boost.
A pipeline set up as this page describes serves both versions.
Division of work
Foundation4 stores and searches the messages. Rocket.Chat decides what each reader may see and presents the results.
| Component | Responsibility |
|---|---|
| Rocket.Chat indexing service | Sends each message to the pipeline as a document, with the message's room, identifier, sender and time as metadata |
| Foundation4 | Splits, embeds and stores the messages, and runs similarity and full-text searches restricted by classification and metadata filter |
| Rocket.Chat server | Builds each search request, restricts the search to the reader's rooms, combines the results, reads each message from the Rocket.Chat database and removes any message outside the reader's rooms |
| Rocket.Chat LLM provider | Generates the optional answer from the messages found. Foundation4 is not involved. |
The access boundary of this integration is the check in the Rocket.Chat server. Each search request carries a metadata filter on the reader's rooms, and Rocket.Chat then reads each result from the Rocket.Chat database and drops any message in a room the reader does not belong to. Rocket.Chat displays the message text from the Rocket.Chat database, so a message deleted in Rocket.Chat is never displayed, even while the message remains in the pipeline.
Search path
The following diagram shows one hybrid search and the optional answer. In Rocket.Chat 8.7 and 8.8, only the similarity search runs.
Pipeline setup
One pipeline holds the messages of one workspace. The pipeline needs the following settings:
- Full-text search.
has_full_text_searchistrue. Hybrid search runs a full-text search, and the setting cannot be changed after the pipeline is created. Rocket.Chat 8.7 and 8.8 do not use full-text search, and Rocket.Chat 8.9 does. - Metadata schema. The schema declares the four message fields as strings, and requires
room_idandmsg_id. A search request can filter only on fields that the schema declares, and a filter on an undeclared field returns HTTP 400 with the messageSchema error. - Classifications. The classifications that the indexing service assigns to messages, described in Classifications of readers.
- Embedding model. The seeded embedding model,
all-MiniLM-L6-v2, was trained on English text. A workspace that writes in other languages uses a multilingual embedding model, described in Embedding models and text splitters. The embedding model cannot be changed after the pipeline is created.
The following request creates the pipeline. $EMBEDDING_MODEL_ID and $TEXT_SPLITTER_ID hold the identifiers of the embedding model and the text splitter, as in First search.
curl -X POST "$FOUNDATION4_URL/pipelines" \
-H "x-api-key: $FOUNDATION4_API_KEY" \
-H "x-api-key-secret: $FOUNDATION4_API_SECRET" \
-H "Content-Type: application/json" \
-d '{
"name": "rocketchat-messages",
"description": "Messages of the Rocket.Chat workspace",
"embedding_model_id": "'"$EMBEDDING_MODEL_ID"'",
"default_text_splitter_id": "'"$TEXT_SPLITTER_ID"'",
"classifications": ["user", "admin"],
"has_full_text_search": true,
"schema": {
"type": "object",
"properties": {
"room_id": {"type": "string"},
"msg_id": {"type": "string"},
"username": {"type": "string"},
"timestamp": {"type": "string"}
},
"required": ["room_id", "msg_id"]
}
}'
Expected result: status 201 and the new pipeline, with has_full_text_search set to true and the four fields in schema. The id of the pipeline is the value of the Pipeline ID setting in Rocket.Chat.
The metadata fields have the following roles:
| Field | Use in Rocket.Chat |
|---|---|
room_id | Filters every search to the reader's rooms, and identifies the room of each result |
msg_id | Identifies the message of each result. A result without msg_id matches no message and is dropped. |
username | Filters searches that use the from: operator |
timestamp | Filters searches that use after: or before:, and ranks recent messages higher when Recency boost is set |
Rocket.Chat also accepts rid for room_id and message_id for msg_id. The schema declares the names that the indexing service sends.
Classifications of readers
Classifications are the attributes that a reader must hold to see a document, as described in Classifications. Rocket.Chat derives the classifications of a reader from Rocket.Chat roles: each search request names the classification user and the name of each global role that the reader holds, such as admin or bot. Room roles, such as room owner or moderator, are not included.
Rocket.Chat requests hierarchical matching (search_type 2) for both searches. Names that the pipeline does not define are ignored, and the classifications that a named classification inherits are included. The pipeline therefore defines the classifications that the indexing service assigns to messages:
user. Every search request namesuser, so a message with the classificationusercan be found by every reader who belongs to the message's room.- Role names. A message with the name of a global role as the classification, such as
admin, can be found only by readers who hold that role.
The classification that each message carries is set by the indexing service, as described in Rocket.Chat message indexing.
Search API key
Rocket.Chat searches with one API key for the workspace. The key holds read and execute permission on the pipeline (permission 5) and no other permission. Scoped keys describes how to create the key and grant the permission. The key that the indexing service uses is a separate key with wider permissions.
Rocket.Chat settings
Rocket.Chat administrators configure AI search in the Search section of the Rocket.Chat AI center. Rocket.Chat's documentation describes the Rocket.Chat side of the setup, including licensing. The following settings take Foundation4 values:
| Setting | Value |
|---|---|
| Pipeline API base URL | The base URL of the Foundation4 API server, with no path, such as http://foundation4ai-api-server.foundation4ai.svc.cluster.local for a Rocket.Chat server in the same Kubernetes cluster |
| Pipeline ID | The id of the pipeline |
| Pipeline API key | The id of the search API key |
| Pipeline API key secret | The secret of the search API key |
| Minimum semantic similarity (%) | The minimum similarity of a similarity search result, from 0 to 100. The default, 0, applies no minimum. |
| Query template | Empty. A template adds text around the reader's query, described in Query template. |
| Search balance | Rocket.Chat 8.9. 0 runs the full-text search only, 100 runs the similarity search only, and values in between run both. The default is 50. |
| Recency boost | Rocket.Chat 8.9. Raises the rank of recent messages. The default, 0, ignores the age of a message. |
AI search is active only when the base URL, the pipeline ID, the key and the secret are all set. With any of the four missing, AI search returns no results and reports no error.
Rocket.Chat checks the address of every outbound request against server-side request forgery (SSRF) rules. The following rules apply to the Pipeline API base URL:
- Host name. The host is an IP address or a name with at least one dot and a final label of letters, such as
foundation4.example.internalor a Kubernetes service name ending insvc.cluster.local. A single-label name, such asfoundation4ai-api-serverorlocalhost, is rejected. - Private addresses. When the host resolves to a private, loopback or link-local address, the host name or IP address is listed in the Rocket.Chat setting SSRF allowlist.
Each search request times out after 10 seconds.
Search requests
Rocket.Chat sends each search as POST /pipelines/{id}/search with the key in the x-api-key and x-api-key-secret headers. Search and retrieval describes the request fields. The following request reproduces a similarity search from Rocket.Chat for a reader who holds the global role admin, belongs to two rooms and searches the search page with a minimum similarity of 0:
curl -X POST "$FOUNDATION4_URL/pipelines/$PIPELINE_ID/search" \
-H "x-api-key: $FOUNDATION4_API_KEY" \
-H "x-api-key-secret: $FOUNDATION4_API_SECRET" \
-H "Content-Type: application/json" \
-d '{
"query": "deploy failed",
"type": "similarity",
"classification": {"classifications": ["user", "admin"], "search_type": 2},
"filters": {"room_id": {"$in": ["GENERAL", "6f1c2a9b8d7e"]}},
"params": {"k": 27, "threshold": 1}
}'
Expected result: status 200 and a JSON array of fragments, closest first. Each fragment carries the message fields in metadata, and score is the cosine distance from the query:
[
{
"id": "0192f7b3-4c5d-7e6f-8a9b-0c1d2e3f4a5b",
"classification": "user",
"metadata": {
"room_id": "GENERAL",
"msg_id": "Xk3pL9qR2sT7vW1yZ",
"username": "alice",
"timestamp": "2026-09-12T10:00:00.000Z"
},
"document_id": "0192f7b2-9a8b-7c6d-8e5f-4a3b2c1d0e9f",
"page_content": "The deploy to staging failed again at the database migration step.",
"version": 1789207205000000,
"expired_at": null,
"score": 0.4313,
"order_id": 0
}
]
The full-text search of hybrid search sends the same query, classifications and filter with "type": "search" and no threshold.
Request fields
Rocket.Chat builds each field of the request as follows:
query. The reader's query, inside the query template when the template is set.classification.userand the reader's global roles, withsearch_type2 for both searches.filters.room_idwith$inand every room of the reader, or$eqwhen the reader has one room. The operatorsin:,from:,after:andbefore:narrowroom_idand addusernameandtimestamp. A reader who belongs to no room receives no results, and Rocket.Chat sends no request.params.k. In Rocket.Chat 8.7 and 8.8, the number of results that the page requests: 5 in the navigation bar search and 9 on the search page, increased by 8 for each additional page up to 50. In hybrid search, three times the requested number, with a minimum of 20 and a maximum of 100, for each of the two searches.params.threshold. For the similarity search only: 1 minus the Minimum semantic similarity setting divided by 100. The similarity score is a cosine distance, where a lower value is a closer match, so a minimum similarity of 70 percent becomes a threshold of 0.3, the largest distance a result may have.
Date filters compare timestamp values as strings. The comparison is chronological only when every stored timestamp uses the format that Rocket.Chat sends in the filter, described in Rocket.Chat message indexing.
Query template
The query template wraps the reader's query in fixed text, with {query} marking the position of the query. Some embedding models expect such an instruction before a search query. Rocket.Chat applies the template to both searches. A full-text search matches only fragments that contain every significant word of the query, so the words of a template must also appear in each matching message. The Query template setting therefore stays empty when Search balance is below 100.
Hybrid ranking
In hybrid search, Rocket.Chat runs the two searches in parallel and combines the two result lists by weighted reciprocal rank fusion (RRF). The rank of a message in each list, rather than the score, determines the combined order, because the similarity score and the full-text score are on different scales. Search and retrieval describes both scores.
Rocket.Chat applies the following rules:
- Rank contribution. A message at rank r in a list contributes w / (60 + r), where w is the weight of the list. The similarity weight is Search balance divided by 100, and the full-text weight is 1 minus the similarity weight. The contributions of the two lists are added.
- One message per result. Each list keeps the best fragment of each message.
- Minimum similarity. Rocket.Chat removes similarity results below the Minimum semantic similarity setting. Full-text results are not affected.
- Recency boost. A Recency boost of R multiplies the combined score by 1 + (R / 100) × 2^(−a / 30), where a is the age of the message in days. A message sent 30 days ago therefore receives half the increase of a message sent today. The age is read from the
timestampmetadata field. - Partial failure. When one of the two searches fails, Rocket.Chat serves the results of the other search and records a warning in the Rocket.Chat log. When both fail, AI search returns no results.
Native hybrid search, with both searches and re-ranking inside Foundation4, is planned. Rocket.Chat then sends one request instead of two.
Answers
When Generate AI answers is on, Rocket.Chat sends the reader's question and the result messages on the page, up to 20 messages, to the LLM provider configured in the LLM providers section of the Rocket.Chat AI center. Rocket.Chat calls the provider's OpenAI-compatible chat completions endpoint directly with the provider's own key. Foundation4 LLMs and agents do not take part, and the Foundation4 search key needs no permission on LLMs.
Troubleshooting
AI search returning no results
- Symptoms. AI search shows no results for queries that match indexed messages, and Rocket.Chat shows no error.
- Diagnosis. Run the search request in Search requests from the host of the Rocket.Chat server, with the Rocket.Chat values for the base URL, pipeline ID and key. A 200 response with fragments points to Rocket.Chat configuration. A 400 response with
Schema errorpoints to the metadata schema. A 401, 403 or 404 response points to the key, the key's permissions or the pipeline ID. Search the Rocket.Chat server log for warnings from AI search. - Cause. One of the four connection settings is empty, the SSRF rules reject the base URL, the schema does not declare a filtered field, or the key lacks read and execute permission on the pipeline.
- Resolution. Set all four connection settings, use a host name that the SSRF rules accept and list a private host in SSRF allowlist, declare the four message fields in the schema, and grant permission 5 on the pipeline to the search key.
- Actions to avoid. Granting the search key permissions on the object type or write permission to test the connection. A key with wider permissions hides the cause and remains in the Rocket.Chat settings.
Full-text search not enabled
- Symptoms. In hybrid search, every reader receives only similarity results.
- Diagnosis.
GET /pipelines/{id}returnshas_full_text_searchset tofalse. A full-text request returns HTTP 501 with the messageFull text search is not enabled for this pipeline. - Cause. The pipeline was created without full-text search. The setting cannot be changed after creation.
- Resolution. Create a new pipeline with
has_full_text_searchset totrue, index the messages into the new pipeline, then change the Pipeline ID setting in Rocket.Chat. - Actions to avoid. Setting Search balance to 100 as a permanent resolution, which removes exact-term matching for error codes, ticket numbers and names.