How to Build a Reusable Knowledge-Lookup Subworkflow
Build one child workflow in Spojit that queries a persistent Knowledge collection and returns a clean answer, then call it as a step from any number of parent support and approval flows.
What This Integration Does
Many of your workflows need the same thing: an answer pulled from your company knowledge. A support flow needs the refund policy, an approval flow needs the spending-limit rule, a triage flow needs the SLA for a customer tier. Copying the same Knowledge query into every workflow means you maintain the prompt, model, and result count in a dozen places. This tutorial shows you how to centralize that logic once. You build a small Subworkflow-friendly child workflow whose only job is to take a question, query a persistent Knowledge collection, and return a structured answer. Every parent flow then calls it with a single Subworkflow node and gets back the same shape of result.
The child runs through its own Manual trigger when invoked: a parent passes a question in as input, the child queries the collection with a Knowledge node in Query mode, and the child returns the answer to the parent through a Response node. The collection is workspace-scoped and persistent, so it stays warm between runs and can be read by any workflow. Because changes to a child take effect immediately for every parent, editing the query prompt or swapping the model in one place updates the behavior everywhere at once. Re-runs are stateless: each call queries the live collection contents at that moment, so newly embedded documents are reflected on the next invocation with no change to the parents.
Prerequisites
- A persistent Knowledge collection that already holds your reference documents (policies, FAQs, product specs). Create one from the Knowledge section of the sidebar with New Collection, then use Upload Document and Upload & Embed. See the related articles below.
- Note the exact collection name and the embedding model it was created with. You always query a collection with the same embedding model it was embedded with.
- Permission to create workflows in your workspace, since the parents pick the child from the workspace list.
- Optional: a Slack connection (Connections -> Add connection -> Slack) if you want a parent flow to post the looked-up answer to a channel.
Step 1: Create the child workflow with a Manual trigger
Create a new workflow named something like kb-lookup. Add a Trigger node and set its type to Manual. A Manual trigger's output is whatever request body is passed in, which is exactly how a Subworkflow node hands data to a child. Decide the input contract now: keep it small and stable so parents can rely on it. A good shape is a single question plus optional context fields.
{
"question": "What is our refund window for damaged goods?",
"audience": "support-agent"
}
Downstream nodes read these as {{ input.question }} and {{ input.audience }}.
Step 2: Query the collection with a Knowledge node
Add a Knowledge node and set its mode to Query. Configure it as follows:
- Collection: pick your persistent collection (not Transient, since you want the long-lived archive that every workflow can read).
- Prompt: a natural-language instruction built from the input, for example
Answer this question using only the company policy documents: {{ input.question }}. If the documents do not contain the answer, say so plainly. - Model: the AI model that synthesizes the answer from the retrieved chunks.
- Result Count: how many chunks to retrieve before synthesis. The default is
5; raise it for broad policy questions, lower it for tightly scoped facts. - Output Variable: name it
lookupso later steps reference{{ lookup }}.
This is the one place the query logic lives. Tuning the prompt or model here changes the behavior for every parent that calls this child.
Step 3: Force a predictable answer shape with a Response Schema
Parents are easier to build when the child always returns the same fields. On the same Knowledge Query node, fill in the optional Response Schema to force structured JSON output instead of free text. Define the fields each parent will consume.
{
"type": "object",
"properties": {
"answer": { "type": "string" },
"found": { "type": "boolean" },
"confidence": { "type": "string", "enum": ["high", "medium", "low"] }
},
"required": ["answer", "found"]
}
With the schema applied, {{ lookup }} resolves to that object, so a parent can branch on {{ lookup.found }} or display {{ lookup.answer }} without parsing prose.
Step 4: Return the result to the parent with a Response node
Add a Response node as the child's final step and set its value to the structured result. You can return the whole object:
{{ lookup }}
Or reshape it first with a Transform node if you want to add the original question back for traceability, for example returning { "question": "{{ input.question }}", "answer": "{{ lookup.answer }}", "found": {{ lookup.found }} }. Whatever the Response node returns becomes the child's final output, and that is what the parent's Subworkflow node receives. Save the child workflow. It is now a reusable lookup that any flow can invoke.
Step 5: Call the child from a parent with a Subworkflow node
Open a parent workflow, for example a support-email flow. At the point where it needs an answer, add a Subworkflow node and configure it:
- Workflow: pick your
kb-lookupchild from the workspace list. - Input: map the parent's data into the child's input contract.
{
"question": "{{ trigger.subject }}",
"audience": "support-agent"
}
When the run reaches this node, the parent pauses, the child runs through its Manual trigger and Knowledge query, and the child's Response value comes back as the Subworkflow node's output. Name that output something like kb so the rest of the parent reads {{ kb.answer }} and {{ kb.found }}. Each child invocation appears as its own entry in execution history, which keeps the parent's trace readable.
Step 6: Act on the answer in the parent
Use the structured result to drive parent logic. A few common patterns:
- Condition node on
{{ kb.found }}: route the true branch to a confident auto-reply, route the false branch to a Human node so a teammate handles questions the knowledge base could not answer. - Slack notification: add a Connector node in Direct mode, pick the Slack connector and the
send-messagetool, and post the looked-up answer to a channel with text likePolicy lookup: {{ kb.answer }}. - Approval context: in an approval flow, place the Subworkflow node before a Human node and include
{{ kb.answer }}in the approval Message so reviewers see the relevant rule inline.
Repeat Step 5 in every other parent that needs the same lookup. You now have one source of truth that many flows reuse.
Step 7: Keep the collection current without touching parents
Because the child queries the live collection, you keep answers fresh simply by maintaining the documents. Add new policies through the Knowledge section with Upload Document and Upload & Embed, or build a separate ingestion workflow that uses a Knowledge node in Embed mode to add documents on a schedule. No parent and no Subworkflow node needs to change: the next call picks up the new content automatically. Spojit treats the child as shared logic, so this stays a single point of maintenance.
Tips
- Keep the child's input contract minimal and stable. A single
questionfield plus one or two optional context fields is easier for every parent to map than a sprawling object. - Tune Result Count per the kind of question you ask most. Broad policy questions benefit from more chunks; single-fact lookups stay sharper with fewer.
- Use the Response Schema to expose a
foundorconfidenceflag so parents can decide when to escalate to a Human node instead of trusting a low-quality answer. - Let Miraxa, the intelligent layer across your automation, scaffold a parent for you with a prompt like "Add a Subworkflow node that calls kb-lookup and connect its output to a Condition node that checks {{ kb.found }}", then fine-tune fields in the properties panel.
Common Pitfalls
- Querying a collection with a different embedding model than it was embedded with returns poor matches. Always pair the same embedding model for embed and query.
- Do not pick Transient as the child's collection. A transient collection is auto-created and discarded per run, so it would be empty for a lookup child that relies on a persisted archive.
- If a parent reads
{{ kb.answer }}but the child returned free text instead of an object, the field will be empty. Confirm the Response Schema is set on the Knowledge node and that the Response node returns the structured value. - Avoid deep nesting. Calling lookup children from children of children makes runs harder to trace and adds latency; keep the lookup one level below each parent.
Testing
Validate the child in isolation first. Open kb-lookup, press Run, and pass a known question in the request body, for example { "question": "What is our refund window?" }. Confirm the Knowledge node returns a sensible answer and that found is true, then try a question your documents do not cover and confirm found comes back false. Once the child is solid, test one parent end to end with a small input, watch both the parent and child entries appear in execution history, and verify the parent's Condition or Slack step behaves on each branch. Only after a parent passes should you wire the same Subworkflow node into your remaining flows.