How to Build a Slack Knowledge Bot That Answers From Your Document Collection

Let a question posted in Slack trigger a workflow in Spojit that searches a persistent Knowledge collection and posts an AI-synthesized answer straight back into the same thread.

What This Integration Does

Teams keep asking the same questions: how does the refund policy work, what is the SLA for tier-two tickets, which warehouse ships to which region. The answers already live in your handbooks, policy PDFs, and runbooks, but nobody wants to dig through a shared drive mid-conversation. This workflow puts those documents one Slack message away. Someone posts a question, Spojit queries a Knowledge collection you have already filled with your documents, and the synthesized answer appears in the thread within seconds, grounded in your own source material rather than a generic model guess.

The run model is event-driven. A Slack app sends an HTTP POST to a Webhook trigger every time the question channel receives a message; that request body becomes {{ input }}. A Knowledge node in Query mode searches your persistent collection and uses Miraxa, the intelligent layer across your automation, to write a grounded reply. A Connector node then calls the Slack connector's send-message tool to post that reply back into the originating thread. The collection is long-lived and read-only at run time, so the workflow leaves no state behind: each message is an independent run, and re-running the same event simply produces the same answer again.

Prerequisites

  • A Slack connection added under Connections -> Add connection, with permission to post messages and read message events in your target channel.
  • A persistent Knowledge collection created in the Knowledge section of the sidebar (New Collection), with your policy or reference documents already uploaded via Upload Document and showing status READY. See the prerequisite step below.
  • A signing secret for the inbound webhook so Spojit can verify requests. You will create a signing connection (scheme Slack or Custom) when configuring the trigger.
  • The Slack channel ID where questions are asked (for example C0123ABCD), visible in Slack channel details.
  • Optional: the ability to register a Slack app endpoint or an automation that POSTs message events to your workflow URL.

Step 1: Prepare the Knowledge collection

Before building the workflow, make sure your documents are searchable. In the sidebar open the Knowledge section, choose New Collection, and give it a clear name such as company-policies. The embedding model is fixed at creation, so accept the default (Gemini Embedding 001) unless you have a reason to change it. Open the collection, use Upload Document to drag in your PDFs, Word files, or Markdown, and click Upload & Embed. Wait until each row in the document table shows status READY rather than PROCESSING. This is a persistent collection: it lives across runs and any workflow in the workspace can query it, which is exactly what you want for a shared answering surface. For a deeper walkthrough see the guide on creating a Knowledge collection and uploading documents to a collection.

Step 2: Add the Webhook trigger

Create a new workflow and set the trigger to Webhook. Spojit gives the workflow a unique URL and returns 202 with an executionId to the caller. Attach a signing connection so only verified requests start a run: pick the Slack scheme if you are wiring this to a Slack app event subscription, or Custom if a middle layer forwards the events with its own HMAC. The parsed JSON body is exposed as {{ input }}. For a Slack-style payload your downstream nodes will read fields such as the channel, the message text, and the thread timestamp. A typical inbound body looks like this:

{
  "event": {
    "type": "message",
    "channel": "C0123ABCD",
    "user": "U0456EFGH",
    "text": "What is our refund window for damaged goods?",
    "ts": "1718870400.001200",
    "thread_ts": "1718870400.001200"
  }
}

Reference the question as {{ input.event.text }}, the channel as {{ input.event.channel }}, and the thread parent as {{ input.event.thread_ts }} in later steps. See setting up a Webhook trigger for connection and dedup details.

Step 3: Optionally filter out noise with a Condition node

Slack can deliver events you do not want to answer, such as the workflow's own replies, edits, or messages with no real question. Add a Condition node after the trigger to keep runs cheap and quiet. A practical check is to continue only when the message contains text and is not a bot echo, for example testing that {{ input.event.text }} is not empty and that {{ input.event.subtype }} is not set. Route the true branch onward to the Knowledge step; let the false branch end the run. This keeps you from paying AI credits on empty or system messages. The guide on using Condition nodes covers the comparison operators available.

Step 4: Query the collection with a Knowledge node

Add a Knowledge node and set its mode to Query. In Collection, pick the persistent collection you prepared in Step 1 (for example company-policies). Do not choose Transient here: Transient collections only hold documents embedded earlier in the same run, whereas you want to search your long-lived archive. Set Prompt to the user's question by templating it in:

Answer this question using only the company documents:
{{ input.event.text }}

If the documents do not contain the answer, say you could not find it
and suggest who to ask. Keep the reply under 120 words and Slack-friendly.

Set Result Count to control how many document chunks feed the answer (the default of 5 is a good start; raise it for broad questions). Pick the synthesis Model for Miraxa to write the reply. Leave Response Schema empty since you want plain prose for Slack, not structured JSON. Finally set Output Variable, for example answer, so the synthesized text is available downstream as {{ answer.content }}. Because the same embedding model is used for the collection and the query automatically, you do not configure embeddings here. See using Knowledge nodes and querying your Knowledge base for more on Query mode.

Step 5: Post the answer back to the thread with a Slack Connector node

Add a Connector node, choose the Slack connector, and use Direct mode so the call is deterministic and costs no extra AI credits. Select the send-message tool and map its inputs. Set channel to {{ input.event.channel }} so the reply lands in the same channel, set text to the synthesized answer {{ answer.content }}, and set thread_ts to {{ input.event.thread_ts }} so the answer threads under the original question instead of cluttering the main channel. The mapped configuration looks like this:

{
  "channel": "{{ input.event.channel }}",
  "text": "{{ answer.content }}",
  "thread_ts": "{{ input.event.thread_ts }}"
}

If the incoming message was not itself in a thread, thread_ts equals the message ts, which still starts a tidy thread off the question. For more on configuring deterministic tool calls, read using Connector nodes in Direct mode and the Slack connector reference.

Step 6: Acknowledge the caller and connect the canvas

Connect the nodes in order: Webhook trigger to Condition, the true branch to the Knowledge node, and the Knowledge node to the Slack Connector node. The Webhook trigger already returns 202 with an executionId the moment the request arrives, so the calling Slack app gets a fast acknowledgment while your run continues in the background; you do not need a Response node for this asynchronous pattern. If your forwarding layer expects a specific synchronous body, you can add a Response node on the true branch to return a short confirmation, but for standard Slack event subscriptions the default acknowledgment is enough. Save the workflow, then enable it so the URL goes live. If you prefer to scaffold quickly, ask Miraxa: "Build a workflow that takes a Slack message from a webhook, queries my company-policies Knowledge collection, and posts the answer back to the thread with send-message," then fine-tune each node in the properties panel.

Tips

  • Keep the Prompt instruction explicit about length and tone so replies stay Slack-sized; long answers read poorly in a thread.
  • Raise Result Count for broad questions and lower it for narrow ones; more chunks improve recall but cost more tokens per run.
  • Re-upload a document with the same file name to overwrite it when policies change; the collection stays current without rebuilding it from scratch.
  • Use the Condition filter to ignore messages from the workflow's own posting identity so the workflow never answers itself in a loop.

Common Pitfalls

  • Querying before documents finish embedding returns thin answers; wait for every document row to show READY, not PROCESSING.
  • Forgetting thread_ts on send-message posts the answer to the main channel instead of the thread, which buries the original question.
  • Slack retries event deliveries if it does not get a fast acknowledgment; rely on the trigger's 202 response and turn on event-id dedup so retries do not produce duplicate answers.
  • Choosing Transient in the Knowledge node by mistake searches nothing, because nothing was embedded in that run; always pick your persistent collection for this workflow.
  • Missing Slack permissions cause the send-message call to fail; confirm the connection can post to the target channel before going live.

Testing

Validate on a private test channel first. Add one or two short documents to the collection, wait for READY, then send a sample request to the workflow URL with a body matching the Slack shape above, including a real channel and thread_ts. Confirm the run completes in the execution history and that the reply appears in the correct thread. Ask a question you know the answer to, then one you know is absent, and check that Miraxa grounds the first and gracefully declines the second. Only after the threaded replies look right should you point the production Slack channel at the workflow and enable it for everyone.

Learn More

Did this answer your question? Thanks for the feedback There was a problem submitting your feedback. Please try again later.